Published on

Express 项目结构最佳实践:中间件 / 服务 / 控制器的组织

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

0. 推荐的 Express 项目结构

  1. 一般结构

    📂 src
    ├── 📂 controllers       # 控制器:负责处理具体的业务逻辑
    ├── 📂 middlewares       # 中间件:负责请求拦截、校验、日志、错误处理等
    ├── 📂 services          # 服务层:封装具体的业务逻辑和数据操作
    ├── 📂 models            # 数据模型:定义数据库 Schema、ORM
    ├── 📂 routes            # 路由定义:组合控制器和中间件
    ├── 📂 utils             # 工具函数:公用方法、加密、格式化等
    ├── 📂 config            # 配置文件:环境变量、数据库配置等
    ├── app.ts               # 应用入口:初始化 Express、挂载中间件和路由
    └── server.ts            # 启动服务
    
  2. 命名规则

    类型变量/文件名示例命名风格
    路由文件user.route.ts业务名+.route.ts
    控制器user.controller.ts业务名+.controller.ts
    服务层user.service.ts业务名+.service.ts
    请求DTOcreateUserDto,文件名 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
  3. 为什么推荐 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         |
+----------------------+

常见问题解答

  1. Q1: 服务层和控制器的区别?
    • 服务层:负责数据操作和业务逻辑,如数据库查询、第三方 API 请求等.
    • 控制器:负责处理 HTTP 请求,包含最少的业务逻辑,更多地依赖服务层.
  2. Q2: 错误处理是否应在服务层?
    • 错误应在控制器中捕获,并交给 next() 传递到错误处理中间件.
    • 服务层应专注于核心逻辑,而非处理 HTTP 相关问题.
  3. Q3: 是否推荐使用 TypeScript?
    • 推荐使用 TypeScript,可通过类型检查减少运行时错误,提升代码的可维护性.