|
@@ -0,0 +1,287 @@
|
|
|
|
|
+# 📐 Express 后端项目架构与开发规范文档(强隔离 · 多端口 · 分模块)
|
|
|
|
|
+
|
|
|
|
|
+## 一、架构设计理念
|
|
|
|
|
+
|
|
|
|
|
+本项目后端采用 **“强隔离 + 多端口 + 分模块”** 的 BFF(Backend For Frontend)风格架构。
|
|
|
|
|
+
|
|
|
|
|
+**核心思想:**
|
|
|
|
|
+- 同一个 `backend` 仓库中,PC 端与移动端的接口实现彼此隔离(避免互相影响)。
|
|
|
|
|
+- PC 端再按业务模块拆分为多个独立的 Express App,每个模块 **独立端口**(满足你“分模块、分端口”的诉求)。
|
|
|
|
|
+- 通用能力(环境变量解析、通用中间件、错误模型、工具函数)集中在 `shared/`,作为唯一允许跨模块复用的区域。
|
|
|
|
|
+
|
|
|
|
|
+**为什么要分端口?**
|
|
|
|
|
+- 模块级独立发布/重启:某个模块改动或异常时不影响其他模块。
|
|
|
|
|
+- 权限与流量隔离:可以在网关或反向代理层按端口做更明确的访问控制/限流。
|
|
|
|
|
+- 未来可拆仓库:把某个模块目录剪切出去即可形成独立服务,迁移成本更低。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 二、目录结构概览
|
|
|
|
|
+
|
|
|
|
|
+后端代码根目录:`backend/src`
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+backend/
|
|
|
|
|
+└── src/
|
|
|
|
|
+ ├── index.ts # [总启动器] 启动多个 Express 实例并监听不同端口
|
|
|
|
|
+ │
|
|
|
|
|
+ ├── apps/ # [应用隔离区] 端 + 模块 的物理隔离
|
|
|
|
|
+ │ ├── pc/ # [💻 PC 端隔离区]
|
|
|
|
|
+ │ │ ├── user/ # PC - 用户模块(独立端口)
|
|
|
|
|
+ │ │ │ ├── app.ts # 创建 Express app(挂中间件/路由/错误处理)
|
|
|
|
|
+ │ │ │ ├── server.ts # 监听端口(从 env 读取)
|
|
|
|
|
+ │ │ │ ├── routes/ # 路由层(Router 组合)
|
|
|
|
|
+ │ │ │ ├── controllers/ # 控制器层(req/res 编排)
|
|
|
|
|
+ │ │ │ ├── services/ # 业务服务层(业务逻辑/聚合)
|
|
|
|
|
+ │ │ │ ├── models/ # 模型/DTO(模块内自洽)
|
|
|
|
|
+ │ │ │ ├── validators/ # 入参校验(模块内)
|
|
|
|
|
+ │ │ │ └── middlewares/ # 模块私有中间件(鉴权/审计等)
|
|
|
|
|
+ │ │ │
|
|
|
|
|
+ │ │ ├── order/ # PC - 订单模块(独立端口)
|
|
|
|
|
+ │ │ └── ... # PC 其他模块(按需增加)
|
|
|
|
|
+ │ │
|
|
|
|
|
+ │ └── mobile/ # [📱 移动端隔离区]
|
|
|
|
|
+ │ ├── user/ # Mobile - 用户模块(独立端口)
|
|
|
|
|
+ │ ├── content/ # Mobile - 内容模块(独立端口)
|
|
|
|
|
+ │ └── ...
|
|
|
|
|
+ │
|
|
|
|
|
+ └── shared/ # [共享区] 唯一允许跨模块复用的区域
|
|
|
|
|
+ ├── config/ # 环境变量、端口映射、运行环境
|
|
|
|
|
+ ├── http/ # 通用 HTTP 能力(中间件/错误处理/响应封装)
|
|
|
|
|
+ ├── errors/ # 通用错误类型(AppError 等)
|
|
|
|
|
+ ├── types/ # 全局类型(仅 truly-shared)
|
|
|
|
|
+ └── utils/ # 工具函数(不可依赖具体业务)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 三、核心开发纪律(必须遵守)
|
|
|
|
|
+
|
|
|
|
|
+### 🚫 1. 绝对禁止跨模块导入(防越界法则)
|
|
|
|
|
+
|
|
|
|
|
+为了保证“模块独立端口”真正成立:
|
|
|
|
|
+- `apps/pc/<module>/` **禁止**导入 `apps/pc/<other-module>/` 的任何文件。
|
|
|
|
|
+- `apps/mobile/<module>/` **禁止**导入 `apps/mobile/<other-module>/` 的任何文件。
|
|
|
|
|
+- `apps/pc/**` 与 `apps/mobile/**` 之间也 **禁止互相导入**。
|
|
|
|
|
+- **唯一例外**:允许导入 `shared/**`。
|
|
|
|
|
+
|
|
|
|
|
+如果双端/多模块存在相似实现:
|
|
|
|
|
+- 业务逻辑允许重复(解耦优先)。
|
|
|
|
|
+- 只有“纯工具 / 纯协议 / 纯错误模型”才放进 `shared/`。
|
|
|
|
|
+
|
|
|
|
|
+### 🚫 2. 禁止在 `src/` 根目录堆业务代码
|
|
|
|
|
+
|
|
|
|
|
+`src/` 根目录只允许:
|
|
|
|
|
+- `index.ts`(总启动器)
|
|
|
|
|
+- `apps/`(隔离区)
|
|
|
|
|
+- `shared/`(共享区)
|
|
|
|
|
+
|
|
|
|
|
+除此之外不应出现具体业务 controller/service/model。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 四、端口与服务拆分规范
|
|
|
|
|
+
|
|
|
|
|
+### 1. 端口规划(建议)
|
|
|
|
|
+
|
|
|
|
|
+使用“端 + 模块”粒度进行端口分配,通过环境变量统一管理。
|
|
|
|
|
+
|
|
|
|
|
+示例(仅示意,可按你的模块数量调整):
|
|
|
|
|
+
|
|
|
|
|
+| 端 | 模块 | 环境变量 | 默认端口 |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| PC | user | `PC_USER_PORT` | 3101 |
|
|
|
|
|
+| PC | order | `PC_ORDER_PORT` | 3102 |
|
|
|
|
|
+| Mobile | user | `MOBILE_USER_PORT` | 3201 |
|
|
|
|
|
+| Mobile | content | `MOBILE_CONTENT_PORT` | 3202 |
|
|
|
|
|
+
|
|
|
|
|
+建议端口分段:
|
|
|
|
|
+- `31xx`:PC
|
|
|
|
|
+- `32xx`:Mobile
|
|
|
|
|
+
|
|
|
|
|
+### 2. URL 规范(即使分端口也要统一风格)
|
|
|
|
|
+
|
|
|
|
|
+每个服务内部的路由建议统一以 `/api` 作为前缀:
|
|
|
|
|
+- `http://localhost:3101/api/...`(PC-user)
|
|
|
|
|
+- `http://localhost:3202/api/...`(Mobile-content)
|
|
|
|
|
+
|
|
|
|
|
+### 3. 是否需要“网关聚合层”
|
|
|
|
|
+
|
|
|
|
|
+如果前端希望只配置一个 baseURL(减少多端口管理成本),可在未来增加一个“API Gateway”(仍在本仓库内):
|
|
|
|
|
+- 作为单端口入口(例如 `3000`),按路径转发到对应模块端口
|
|
|
|
|
+- 该网关只做转发与鉴权,不承载业务逻辑
|
|
|
|
|
+
|
|
|
|
|
+是否立刻引入取决于你的前端请求层如何管理多个 baseURL;本规范不强制。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 五、模块内部代码分层规范
|
|
|
|
|
+
|
|
|
|
|
+每个模块建议遵循固定分层,以降低心智负担:
|
|
|
|
|
+
|
|
|
|
|
+### 1. `server.ts`(端口监听层)
|
|
|
|
|
+
|
|
|
|
|
+职责:
|
|
|
|
|
+- 读取本模块端口(env)
|
|
|
|
|
+- `app.listen(...)`
|
|
|
|
|
+- 打印启动信息(可选)
|
|
|
|
|
+
|
|
|
|
|
+要求:
|
|
|
|
|
+- 不写任何业务逻辑
|
|
|
|
|
+
|
|
|
|
|
+### 2. `app.ts`(装配层)
|
|
|
|
|
+
|
|
|
|
|
+职责:
|
|
|
|
|
+- 创建 `express()` 实例
|
|
|
|
|
+- 挂载通用中间件(`cors` / `express.json` / request id 等)
|
|
|
|
|
+- 挂载路由(`routes/`)
|
|
|
|
|
+- 挂载统一错误处理(来自 `shared/http`)
|
|
|
|
|
+
|
|
|
|
|
+### 3. `routes/`(路由层)
|
|
|
|
|
+
|
|
|
|
|
+职责:
|
|
|
|
|
+- 定义 URL 与 controller 映射
|
|
|
|
|
+- 只做“路由组织”,不写业务逻辑
|
|
|
|
|
+
|
|
|
|
|
+### 4. `controllers/`(控制器层)
|
|
|
|
|
+
|
|
|
|
|
+职责:
|
|
|
|
|
+- 解析请求参数(path/query/body)
|
|
|
|
|
+- 调用 service
|
|
|
|
|
+- 统一响应格式
|
|
|
|
|
+
|
|
|
|
|
+要求:
|
|
|
|
|
+- controller 内不直接写复杂业务逻辑
|
|
|
|
|
+- 外部依赖(数据库/第三方接口)应在 service 或 client 中处理
|
|
|
|
|
+
|
|
|
|
|
+### 5. `services/`(业务层)
|
|
|
|
|
+
|
|
|
|
|
+职责:
|
|
|
|
|
+- 编排业务流程
|
|
|
|
|
+- 聚合多个数据源
|
|
|
|
|
+- 产出业务结果或抛出业务错误
|
|
|
|
|
+
|
|
|
|
|
+### 6. `models/` 与 `validators/`
|
|
|
|
|
+
|
|
|
|
|
+建议:
|
|
|
|
|
+- `models/` 存 DTO、业务对象、接口响应结构类型
|
|
|
|
|
+- `validators/` 存入参校验逻辑(没有引入第三方校验库时,也可以写轻量手写校验)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 六、`shared/` 共享区规范
|
|
|
|
|
+
|
|
|
|
|
+`shared/` 的存在是为了减少“不可避免的重复”,但必须克制使用。
|
|
|
|
|
+
|
|
|
|
|
+允许放入 `shared/` 的内容:
|
|
|
|
|
+- 环境变量读取与校验(例如 `shared/config/env.ts`)
|
|
|
|
|
+- 通用错误类型(例如 `shared/errors/app-error.ts`)
|
|
|
|
|
+- 通用 HTTP 中间件(例如统一错误处理、统一响应包装)
|
|
|
|
|
+- 与业务无关的工具函数(例如日期格式化、字符串处理)
|
|
|
|
|
+- 与端无关的协议类型(例如分页参数 `PageQuery`)
|
|
|
|
|
+
|
|
|
|
|
+禁止放入 `shared/` 的内容:
|
|
|
|
|
+- 任何“某个模块独有”的业务规则
|
|
|
|
|
+- 与某个端强绑定的鉴权逻辑(PC / Mobile 通常不同)
|
|
|
|
|
+- 依赖某个模块模型的工具(会引入反向耦合)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 七、环境变量与配置规范
|
|
|
|
|
+
|
|
|
|
|
+### 1. `.env` 示例(建议)
|
|
|
|
|
+
|
|
|
|
|
+后端已使用 `dotenv`,建议将端口集中到环境变量中管理:
|
|
|
|
|
+
|
|
|
|
|
+```ini
|
|
|
|
|
+NODE_ENV=development
|
|
|
|
|
+
|
|
|
|
|
+PC_USER_PORT=3101
|
|
|
|
|
+PC_ORDER_PORT=3102
|
|
|
|
|
+
|
|
|
|
|
+MOBILE_USER_PORT=3201
|
|
|
|
|
+MOBILE_CONTENT_PORT=3202
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. 端口读取规范
|
|
|
|
|
+
|
|
|
|
|
+约定:每个模块只读取属于自己的端口变量,避免“误用其他模块端口”。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 八、总启动器 `src/index.ts` 的职责边界
|
|
|
|
|
+
|
|
|
|
|
+`src/index.ts` 作为“进程内多端口启动器”,职责应保持克制:
|
|
|
|
|
+- 加载环境变量(`dotenv.config()`)
|
|
|
|
|
+- 启动各模块 server(例如 import 并调用 `startPcUserServer()`)
|
|
|
|
|
+- 处理进程级事件(例如 `SIGINT` 优雅退出,按需)
|
|
|
|
|
+
|
|
|
|
|
+它不应该:
|
|
|
|
|
+- 直接注册路由
|
|
|
|
|
+- 承载业务逻辑
|
|
|
|
|
+- 写模块内部的中间件细节
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 九、命名规范
|
|
|
|
|
+
|
|
|
|
|
+| 层级/类型 | 命名约定 | 示例 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| 模块目录 | kebab 或 plain(统一即可) | `apps/pc/user` |
|
|
|
|
|
+| 路由文件 | `[resource].routes.ts` | `routes/user.routes.ts` |
|
|
|
|
|
+| 控制器 | `[resource].controller.ts` | `controllers/user.controller.ts` |
|
|
|
|
|
+| 服务 | `[domain].service.ts` | `services/user.service.ts` |
|
|
|
|
|
+| 模型/DTO | `[entity].model.ts` / `[name].dto.ts` | `models/user.model.ts` |
|
|
|
|
|
+| 校验 | `[name].validator.ts` | `validators/create-user.validator.ts` |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 十、接口实现建议(最小可落地模板)
|
|
|
|
|
+
|
|
|
|
|
+每个模块建议至少包含:
|
|
|
|
|
+- 健康检查:`GET /api/health`
|
|
|
|
|
+- 版本信息:`GET /api/version`(可选)
|
|
|
|
|
+- 统一错误返回格式(来自 `shared/http`)
|
|
|
|
|
+
|
|
|
|
|
+统一响应格式(建议):
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "success": true,
|
|
|
|
|
+ "data": {},
|
|
|
|
|
+ "error": null
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+错误响应(建议):
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "success": false,
|
|
|
|
|
+ "data": null,
|
|
|
|
|
+ "error": {
|
|
|
|
|
+ "message": "xxx",
|
|
|
|
|
+ "code": "SOME_ERROR_CODE"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 十一、迁移当前 `backend/src/index.ts` 的建议落点
|
|
|
|
|
+
|
|
|
|
|
+你当前 `src/index.ts` 里包含了 `/api/chat` 与 `/api/health`,并且是单端口单应用形态。
|
|
|
|
|
+
|
|
|
|
|
+按本规范迁移时(建议,不要求一次做完):
|
|
|
|
|
+- 如果 `/api/chat` 属于 PC 或 Mobile 的某个模块:移动到对应的 `apps/<端>/<模块>/` 下
|
|
|
|
|
+- `src/index.ts` 只保留“启动多个模块 server”逻辑
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 十二、维护指南(接手/扩展时怎么做)
|
|
|
|
|
+
|
|
|
|
|
+1. 新增接口:先明确属于哪个端(pc/mobile),再明确属于哪个模块目录。
|
|
|
|
|
+2. 新增模块:在 `apps/<端>/<模块>/` 建目录,并新增端口环境变量。
|
|
|
|
|
+3. 改动共享能力:优先评估是否会影响所有模块;共享区变更需要更严格的自测。
|
|
|
|
|
+4. 需要复用但又不适合进 `shared/`:宁可复制一份(保持隔离),不要为了复用而耦合。
|
|
|
|
|
+
|