- Published on
Express 项目结构最佳实践:中间件 / 服务 / 控制器的组织
- Authors
- Name
- Shelton Ma
0. 推荐的 Express 项目结构
一般结构
📂 src ├── 📂 controllers # 控制器:负责处理具体的业务逻辑 ├── 📂 middlewares # 中间件:负责请求拦截、校验、日志、错误处理等 ├── 📂 services # 服务层:封装具体的业务逻辑和数据操作 ├── 📂 models # 数据模型:定义数据库 Schema、ORM ├── 📂 routes # 路由定义:组合控制器和中间件 ├── 📂 utils # 工具函数:公用方法、加密、格式化等 ├── 📂 config # 配置文件:环境变量、数据库配置等 ├── app.ts # 应用入口:初始化 Express、挂载中间件和路由 └── server.ts # 启动服务
命名规则
类型 变量/文件名示例 命名风格 路由文件 user.route.ts 业务名+.route.ts 控制器 user.controller.ts 业务名+.controller.ts 服务层 user.service.ts 业务名+.service.ts 请求DTO createUserDto,文件名 create-user.dto.ts 小驼峰变量,中横线文件名,后缀统一用 .dto.ts 校验器 user.validator.ts 业务名.validator.ts 模型文件 user.model.ts 业务名.model.ts 工具函数 logger.ts、time.ts 文件名为功能名 中间件 auth.middleware.ts 功能.middleware.ts 为什么推荐 user.validator.ts
文件归类清晰: 当你在 validators/ 文件夹下,一眼可以看到所有 xxx.validator.ts,全是一个风格. IDE 搜索方便:搜 *.validator.ts,可以直接列出所有校验器.
1. 中间件 (middlewares/)
- 请求校验、权限验证、日志记录、错误处理等
- 处理请求/响应前的逻辑
- 通过 next() 将流程传递到下一个中间件或控制器
2. 服务 (services/)
- 负责数据操作(如数据库 CRUD、API 调用)
- 处理复杂的业务逻辑
- 让控制器保持更专注于 HTTP 请求处理
3. 控制器 (controllers/)
- 处理请求 (req)、响应 (res) 和异常 (next)
- 通过调用服务完成具体的业务逻辑
- 控制器尽量保持“薄”,只负责路由与服务的连接
4. 路由 (routes/)
- 将 URL 路径映射到对应的控制器
- 在路由中引入所需的中间件
5. 应用入口 (app.ts)
import express from 'express';
import userRoutes from './routes/userRoutes';
import { errorHandler } from './middlewares/errorHandler';
const app = express();
// === 中间件 ===
app.use(express.json()); // 解析 JSON 请求体
app.use('/api/users', userRoutes); // 用户路由
app.use(errorHandler); // 错误处理器
export default app;
6. 启动服务 (server.ts)
加载 app.ts 并监听指定端口
import app from './app';
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`🚀 Server is running on http://localhost:${PORT}`);
});
7. 流程图
Incoming Request
│
▼
+----------------------+
| Global Middlewares |
+----------------------+
│
▼
+----------------------+
| Routes |
+----------------------+
│
▼
+----------------------+
| Controller |
+----------------------+
│
▼
+----------------------+
| Service |
+----------------------+
│
▼
+----------------------+
| Database |
+----------------------+
常见问题解答
- Q1: 服务层和控制器的区别?
- 服务层:负责数据操作和业务逻辑,如数据库查询、第三方 API 请求等.
- 控制器:负责处理 HTTP 请求,包含最少的业务逻辑,更多地依赖服务层.
- Q2: 错误处理是否应在服务层?
- 错误应在控制器中捕获,并交给 next() 传递到错误处理中间件.
- 服务层应专注于核心逻辑,而非处理 HTTP 相关问题.
- Q3: 是否推荐使用 TypeScript?
- 推荐使用 TypeScript,可通过类型检查减少运行时错误,提升代码的可维护性.