工程效率与质量 / 进阶

Monorepo 不只是把仓库放在一起:用工作区守住模块边界

从目录职责、依赖方向和发布边界出发,搭建一个不会随着项目增长而失控的 Monorepo。

先定义职责,再创建 packages

Monorepo 最常见的问题不是包太多,而是每个包都能随意引用其他包。先把应用、业务能力、基础设施和纯工具分层,再规定依赖只能由上层流向下层。目录名只是提示,真正的边界还要交给 lint 规则和持续集成验证。

EXAMPLE / 01Monorepo
apps/
  web/
  admin/
packages/
  domain-order/
  ui/
  config-eslint/
  config-typescript/

让依赖关系可以被机器检查

给内部包统一使用 workspace 协议,避免本地开发和发布版本不一致。再通过 ESLint 的 import/no-restricted-paths 或 dependency-cruiser 声明禁止方向,例如 UI 包不能反向依赖具体业务包。

子包用到的依赖必须写进自己的 package.json。不要因为依赖被提升到根目录就省略声明,否则切换包管理器或独立发布时会立即暴露问题。

EXAMPLE / 02Monorepo
{
  "dependencies": {
    "@repo/domain-order": "workspace:^",
    "@repo/ui": "workspace:^"
  }
}

只构建受影响的部分

缓存和增量构建只有在任务输入定义准确时才可靠。构建任务应包含源码、配置和依赖包输出;测试任务通常还要包含测试配置。先保证缓存命中不会返回错误结果,再追求流水线速度。

内部包拥有明确的 exports;CI 检查循环依赖和越层引用;变更集只发布真正变化的包;从干净环境验证一次完整安装与构建。