Files
gongxue-base/openspec/changes/migrate-to-turborepo/design.md

5.2 KiB
Raw Blame History

Context

当前项目为双应用平面结构:backend/NestJS + TypeORMfrontend/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 — 通用 compilerOptionsstrictNullChecksskipLibCheckforceConsistentCasingInFileNamesesModuleInterop 等)
  • nestjs.json — 继承 base + NestJS 特有(experimentalDecoratorsemitDecoratorMetadatadeclaration
  • react-vite.json — 继承 base + 前端特有(jsx: react-jsxmoduleResolution: bundler

6. Turbo 流水线设计

{
  "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-depsreact-refresh 规则丢失 明确记录缺失规则,依赖 TypeScript compiler 和 code review 兜底
npm workspaces hoisting 行为变化 子项目可能访问到 hoisted 的不兼容版本 Turborepo 严格模式下按 workspace 隔离;迁移后全量测试
Docker 构建 context 路径变更 docker-compose.ymlbuild: ./backend 需改为 build: ./apps/server 迁移后 docker compose build 验证
根 node_modules 现有依赖冲突 @fission-ai/openspec 与子项目依赖可能 hoisting 冲突 openspec 保留在根,子项目依赖在各自 workspace 中声明