Files
gongxue-base/openspec/changes/archive/2026-07-02-migrate-to-turborepo/design.md

113 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
当前项目为双应用平面结构:`backend/`NestJS + TypeORM`frontend/`React 19 + Vite通过 `docker-compose.yml` 编排部署。根 `package.json` 仅包含两个 `cd` 子目录的脚本,无统一构建编排能力。
随着业务扩展规划(学生端 `apps/student/` 等新应用),当前结构面临以下问题:
- 无共享配置机制,前后端各自维护 ESLint/TypeScript 配置
- 无构建缓存和并行能力CI 时间随应用增加线性增长
- 新增应用无标准目录约定,结构逐渐混乱
## Goals / Non-Goals
**Goals:**
- 建立标准 Turborepo monorepo 目录结构apps/ + packages/
- 引入 Turborepo 构建流水线与缓存,支持并行构建
- npm workspaces 统一依赖管理
- 抽取共享 TypeScript 配置到独立 package
- 代码格式化迁移到 oxfmt统一
- 前端 Linting 迁移到 oxlint更快后端保留 ESLint保障 NestJS 覆盖)
- Docker Compose 构建上下文适配新目录结构
**Non-Goals:**
- 不拆分后端微服务
- 不引入 pnpm/yarn 包管理器
- 不重构业务代码逻辑src/ 零变更)
- 不修改 Docker Compose 服务编排逻辑
- 暂不抽取 shared-types 包
## Decisions
### 1. 选择 Turborepo 而非 Nx/Lerna
| 维度 | Turborepo | Nx | Lerna |
|------|-----------|-----|-------|
| 构建缓存 | ✅ 内置(内容哈希) | ✅ 内置 | ⚠️ 需插件 |
| 并行执行 | ✅ 自动拓扑排序 | ✅ 自动 | ⚠️ 手动配置 |
| 配置复杂度 | 低(单一 turbo.json | 中nx.json + project.json | 低 |
| 生态 | Vercel 维护,与 Vite 天然契合 | 功能最强但重 | 社区为主 |
**选择 Turborepo**:项目前端使用 Vite同属 Vercel 生态),配置简洁,满足当前规模需求且不过度设计。
### 2. 选择 npm workspaces用户指定
用户要求沿用 npm。npm workspaces 自 v8+ 已成熟,支持 `--workspace` 标志,与 Turborepo 完全兼容。
### 3. 目录结构选择apps/ + packages/
采用 Turborepo 官方推荐结构:
```
gongxue-base/
├── apps/
│ ├── server/ # ← 当前 backend/ 移入API 服务)
│ ├── admin/ # ← 当前 frontend/ 移入(管理后台)
│ └── student/ # 未来:学生端
├── packages/
│ └── typescript-config/ # 共享 TS 配置
├── package.json # workspaces 声明 + 根脚本
├── turbo.json # Turborepo 流水线
├── .oxfmtrc.json # oxfmt 全局配置
├── oxlint.config.ts # oxlint 全局配置
└── docker-compose.yml # 调整构建 context
```
### 4. oxlint / oxfmt 工具链策略
**oxfmt**:统一替换 Prettier。oxfmt 与 Prettier 格式输出高度兼容,配置项映射简单。`.oxfmtrc.json` 放在根目录。
**oxlint**:采用混合策略:
- **前端apps/admin**:全面切换 oxlint。现有规则映射
- `typescript-eslint` → oxlint 内置 TypeScript 规则 ✅
- `react-hooks/rules-of-hooks` → oxlint 内置 ✅
- `react-hooks/exhaustive-deps` → ⚠️ oxlint 不支持,在 oxlint.config.ts 中禁用对应检查,接受 IDE 级补充
- `react-refresh` → ❌ 无等效规则,损失较小(仅 HMR 时的 export 检查)
- **后端apps/server**:保留 ESLint。NestJS 特有的装饰器类型检查和依赖注入规则(`@typescript-eslint/no-unsafe-*` 系列)在 oxlint 中无等效覆盖,贸然切换风险较高。
### 5. 共享 TypeScript 配置设计
`packages/typescript-config/` 提供三个预设:
- `base.json` — 通用 compilerOptions`strictNullChecks``skipLibCheck``forceConsistentCasingInFileNames``esModuleInterop` 等)
- `nestjs.json` — 继承 base + NestJS 特有(`experimentalDecorators``emitDecoratorMetadata``declaration`
- `react-vite.json` — 继承 base + 前端特有(`jsx: react-jsx``moduleResolution: bundler`
### 6. Turbo 流水线设计
```json
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": { "dependsOn": ["^build"] },
"test": { "dependsOn": ["build"] },
"format": { "cache": false },
"typecheck": { "dependsOn": ["^build"] }
}
}
```
## Risks / Trade-offs
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 目录迁移导致所有路径引用失效 | import 路径、Dockerfile、CI 脚本全部需更新 | tasks 中拆分独立的"目录迁移"步骤,逐项验证 |
| oxlint 覆盖不足导致部分规则缺失 | `exhaustive-deps``react-refresh` 规则丢失 | 明确记录缺失规则,依赖 TypeScript compiler 和 code review 兜底 |
| npm workspaces hoisting 行为变化 | 子项目可能访问到 hoisted 的不兼容版本 | Turborepo 严格模式下按 workspace 隔离;迁移后全量测试 |
| Docker 构建 context 路径变更 | `docker-compose.yml``build: ./backend` 需改为 `build: ./apps/server` | 迁移后 `docker compose build` 验证 |
| 根 node_modules 现有依赖冲突 | `@fission-ai/openspec` 与子项目依赖可能 hoisting 冲突 | openspec 保留在根,子项目依赖在各自 workspace 中声明 |