2.3 KiB
2.3 KiB
ADR-005: Monorepo 布局(pnpm workspace,apps/server + apps/web + packages/shared)
Status: Accepted (2026-08-26)
Background
后端(Express)与前端(Vue3)同用 TypeScript,且有大量共享的领域类型(由 zod schema 经 z.infer 产出)、zod 数据 schema、统一响应 ApiResponse<T>、错误码、枚举。若前后端各自复制一份类型定义,一旦模型变更会出现「同一模型多处漂移」,违背 spec-as-contract(契约应单点可寻址)。
Decision
采用 pnpm workspace 单仓多包:
gwy-exam/
├── pnpm-workspace.yaml
├── package.json # 仅脚本 + workspace,不装业务依赖
├── tsconfig.base.json # 共享 strict 编译基座
├── apps/
│ ├── server/ # Express 5 + TS + ESM
│ └── web/ # Vue3 + Vite + TS
└── packages/
└── shared/ # 领域类型 + zod schema + ApiResponse + 枚举 + 常量
类型契约、zod schema、ApiResponse<T>、错误码联合全部放 packages/shared,apps/server 与 apps/web 通过 workspace 引用共享。
为什么选 monorepo 而非独立仓
- 前后端同 JS/TS,
packages/shared是天然共享层,独立仓需额外发布/维护两份。 - 契约类型单点维护,防漂移(spec-as-contract:契约可寻址、可审计)。
- 一次
pnpm install、一次 CI,开发与验收路径单一。
分层依赖(对照 code-organization.md)
apps/server依赖packages/shared(向下)。apps/web依赖packages/shared(向下)。packages/shared不依赖任何 app,保持纯净、可被任何一方 import 而无环。
Consequences
- 正面:类型单一来源、契约一致、前后端并行开发不踩、一次安装。
- 负面:单仓体积稍大;
packages/shared须保持无业务、无 app 依赖(否则环),需纪律约束。 - 权衡:个人工具规模下,monorepo 的成本(配置 pnpm workspace + 共享 tsconfig)远低于收益(类型一致性)。若未来拆出多应用,可无损演进。
Related ADRs
- ADR-001(Express)/ ADR-002(Vue+Vite)
- ADR-004(全 TS strict — 与 shared 类型强绑定)
- ADR-006(垂直切片 — shared 作为单一契约源的承载层)