47 lines
2.3 KiB
Markdown
47 lines
2.3 KiB
Markdown
# 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 而非独立仓
|
||
1. 前后端同 JS/TS,`packages/shared` 是天然共享层,独立仓需额外发布/维护两份。
|
||
2. 契约类型单点维护,防漂移(spec-as-contract:契约可寻址、可审计)。
|
||
3. 一次 `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 作为单一契约源的承载层)
|