单仓库架构设计Skill monorepo-architecture

这个技能提供关于单仓库(Monorepo)架构的全面指导,帮助设计、构建和管理单仓库结构,包括工作空间组织、依赖管理、版本控制和最佳实践,适用于从小型项目到企业级应用程序的软件开发。关键词:单仓库、代码管理、软件架构、依赖管理、版本控制、工作空间配置、最佳实践、软件开发。

架构设计 0 次安装 0 次浏览 更新于 3/25/2026

名称: 单仓库架构 用户可调用: false 描述: 在设计单仓库结构、组织包或迁移到单仓库架构时使用,包括管理依赖和可扩展工作空间配置的架构模式。 允许工具:

  • 读取
  • 写入
  • 编辑
  • Bash
  • Glob
  • Grep

单仓库架构技能

概述

这个技能提供关于设计和构建单仓库(monorepo)的全面指导,包括工作空间组织、依赖管理、版本控制策略和从小型项目到企业应用程序的架构模式。

单仓库 vs 多仓库

何时选择单仓库

单仓库在以下情况中是有益的:

  • 代码共享频繁:多个项目共享公共库、工具或组件
  • 需要原子变更:变更跨越多个包并需要一起部署
  • 统一工具:所有项目受益于一致的代码检查、测试和构建流程
  • 团队协作:团队跨项目边界工作,需要查看相关代码
  • 版本同步:相关包应保持版本对齐
  • 大规模重构:跨项目的大规模重构是常见的
  • 单一事实来源:所有代码、文档和工具集中在一个位置

何时选择多仓库

多仓库在以下情况中是有益的:

  • 独立发布周期:项目按完全不同的时间表部署
  • 不同技术栈:项目使用不兼容的工具或语言
  • 访问控制:不同团队需要隔离访问单独的代码库
  • 仓库大小顾虑:合并的代码库太大,难以高效管理
  • 外部依赖:项目由不同组织维护
  • 简单项目结构:单仓库工具的开销超过益处

权衡

单仓库优势

  • 简化依赖管理
  • 更容易跨边界重构
  • 统一的工具和标准
  • 更好的代码可发现性
  • 跨项目的原子提交
  • 单一的CI/CD管道

单仓库挑战

  • 仓库大小增长
  • CI/CD复杂性
  • 构建时间管理
  • Git性能在规模下
  • 工具要求
  • 开发者的学习曲线

仓库结构模式

基于包的结构

按技术层或包类型组织。

我的单仓库/
├── 应用/
│   ├── 网页/                 # Next.js 网页应用
│   ├── 移动端/              # React Native 应用
│   └── API/                 # Node.js API 服务器
├── 包/
│   ├── 用户界面/                  # 共享UI组件
│   ├── 工具/               # 共享工具
│   ├── 配置/              # 共享配置
│   └── 类型/               # 共享TypeScript类型
├── 服务/
│   ├── 身份验证/                # 身份验证服务
│   ├── 支付/            # 支付处理
│   └── 通知/       # 通知服务
└── 工具链/
    ├── eslint配置/       # ESLint 配置
    └── tsconfig/            # TypeScript 配置

最适合:技术分离、共享库焦点、平台多样性。

基于域的结构

按业务域或功能组织。

我的单仓库/
├── 域/
│   ├── 用户/
│   │   ├── API/            # 用户API
│   │   ├── 网页/            # 用户网页UI
│   │   ├── 移动端/         # 用户移动端UI
│   │   └── 共享/         # 用户共享代码
│   ├── 计费/
│   │   ├── API/
│   │   ├── 网页/
│   │   └── 共享/
│   └── 分析/
│       ├── API/
│       ├── 网页/
│       └── 共享/
└── 共享/
    ├── 用户界面/                 # 跨域UI组件
    ├── 工具/              # 跨域工具
    └── 配置/             # 跨域配置

最适合:域驱动设计、团队按功能拥有、微服务架构。

混合结构

结合基于包和基于域的方法。

我的单仓库/
├── 应用/
│   ├── 客户门户/    # 面向客户的应用程序
│   └── 管理仪表板/    # 管理应用程序
├── 功能/
│   ├── 身份验证/               # 身份验证功能
│   ├── 结账/           # 结账功能
│   └── 库存/          # 库存功能
├── 包/
│   ├── 用户界面/                 # 共享UI库
│   ├── API客户端/         # API客户端库
│   └── 分析/          # 分析库
└── 基础设施/
    ├── 数据库/           # 数据库工具
    ├── 消息传递/          # 消息队列
    └── 部署/         # 部署配置

最适合:复杂组织、平衡技术和域关注。

工作空间配置

NPM 工作空间

使用原生NPM工作空间的基本设置。

{
  "name": "我的单仓库",
  "private": true,
  "workspaces": [
    "应用/*",
    "包/*"
  ],
  "scripts": {
    "开发": "npm run 开发 --workspaces",
    "构建": "npm run 构建 --workspaces",
    "测试": "npm run 测试 --workspaces"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "eslint": "^8.54.0"
  }
}

关键特性

  • 原生NPM支持(v7+)
  • 简单配置
  • 自动工作空间链接
  • 共享依赖提升
  • 工作空间特定命令

Yarn 工作空间

Yarn的工作空间实现,具有额外功能。

{
  "name": "我的单仓库",
  "private": true,
  "workspaces": {
    "packages": [
      "应用/*",
      "包/*"
    ],
    "nohoist": [
      "**/react-native",
      "**/react-native/**"
    ]
  },
  "packageManager": "yarn@3.6.4"
}

使用 .yarnrc.yml 用于Yarn Berry:

nodeLinker: node-modules

plugins:
  - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
    spec: "@yarnpkg/plugin-workspace-tools"

enableGlobalCache: true

compressionLevel: mixed

关键特性

  • 快速安装
  • 插件系统(Yarn Berry)
  • 高级工作空间命令
  • 对特定依赖不提升
  • 零安装能力

PNPM 工作空间

PNPM的高效工作空间实现。

# pnpm-workspace.yaml
packages:
  - '应用/*'
  - '包/*'
  - '服务/*'
  - '!**/测试/**'

使用工作空间特定的 .npmrc

# .npmrc
shared-workspace-lockfile=true
link-workspace-packages=true
prefer-workspace-packages=true
strict-peer-dependencies=false
auto-install-peers=true

关键特性

  • 内容可寻址存储
  • 严格的 node_modules 结构
  • 比NPM/Yarn更快
  • 高效的磁盘空间使用
  • 内置单仓库支持

Cargo 工作空间(Rust)

Rust单仓库工作空间配置。

# Cargo.toml(根)
[workspace]
members = [
    "crates/核心",
    "crates/API",
    "crates/CLI",
]
exclude = ["archived/*"]

[workspace.package]
version = "1.0.0"
edition = "2021"
license = "MIT"

[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.35", features = ["full"] }

单个crate:

# crates/核心/Cargo.toml
[package]
name = "我的核心"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
serde.workspace = true
tokio.workspace = true
我的API = { path = "../API" }

依赖管理

内部包依赖

指定对其他工作空间包的依赖。

{
  "name": "@我的组织/网页应用",
  "version": "1.0.0",
  "dependencies": {
    "@我的组织/用户界面": "workspace:*",
    "@我的组织/工具": "workspace:^",
    "@我的组织/API客户端": "1.2.3",
    "react": "^18.2.0"
  }
}

工作空间协议变体

  • workspace:* - 工作空间中的任何版本
  • workspace:^ - 兼容版本(语义版本号插入符号)
  • workspace:~ - 补丁级别版本(语义版本号波浪号)
  • 特定版本 - 确切的工作空间版本

共享依赖提升

配置依赖如何提升到根目录。

{
  "name": "我的单仓库",
  "workspaces": {
    "packages": ["包/*"],
    "nohoist": [
      "**/react-native",
      "**/react-native/**",
      "**/@babel/**"
    ]
  }
}

提升策略

  • 完全提升:所有公共依赖在根目录(默认)
  • 选择性提升:特定包提升,其他隔离
  • 不提升:每个包有隔离的依赖
  • 公共提升:仅公共依赖提升

版本同步

保持相关依赖在包之间同步。

{
  "name": "我的单仓库",
  "private": true,
  "syncpack": {
    "semverGroups": [
      {
        "range": "",
        "dependencies": ["react", "react-dom"],
        "packages": ["**"]
      }
    ],
    "versionGroups": [
      {
        "label": "React 生态系统必须匹配",
        "dependencies": ["react", "react-dom"],
        "dependencyTypes": ["prod", "dev"],
        "pinVersion": "18.2.0"
      }
    ]
  }
}

使用工具如 syncpack 强制执行一致性:

# 检查版本不一致
pnpm syncpack list-mismatches

# 修复版本不一致
pnpm syncpack fix-mismatches

# 更新所有版本
pnpm syncpack update

单仓库中的对等依赖

正确处理工作空间包之间的对等依赖。

{
  "name": "@我的组织/用户界面",
  "version": "1.0.0",
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "peerDependenciesMeta": {
    "react-dom": {
      "optional": true
    }
  },
  "devDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

最佳实践

  • 在共享库中声明对等依赖
  • 将对等依赖作为开发依赖用于测试
  • 使用 peerDependenciesMeta 用于可选对等依赖
  • 记录对等依赖要求
  • 测试最小和最大对等版本

代码组织

共享代码模式

组织共享代码以实现最大可重用性。

包/
├── 用户界面/
│   ├── 源代码/
│   │   ├── 组件/      # 可重用组件
│   │   ├── 钩子/           # 自定义React钩子
│   │   ├── 样式/          # 共享样式
│   │   └── 索引.ts         # 公共API
│   └── package.json
├── 工具/
│   ├── 源代码/
│   │   ├── 字符串/          # 字符串工具
│   │   ├── 日期/            # 日期工具
│   │   ├── 验证/      # 验证函数
│   │   └── 索引.ts         # 公共API
│   └── package.json
└── 配置/
    ├── eslint配置/
    ├── tsconfig/
    └── prettier配置/

共享库设计

  • 通过桶导出清晰的公共API
  • 最小化外部依赖
  • 良好文档化的接口
  • 全面的单元测试
  • 语义版本控制
  • 变更日志维护

跨包类型共享

高效共享TypeScript类型。

// 包/类型/源代码/用户.ts
export interface 用户 {
  id: 字符串;
  电子邮件: 字符串;
  名称: 字符串;
  角色: 用户角色;
}

export enum 用户角色 {
  管理员 = 'admin',
  用户 = 'user',
  访客 = 'guest',
}

export type 创建用户输入 = Omit<用户, 'id'>;
export type 更新用户输入 = Partial<创建用户输入>;
// 包/类型/package.json
{
  "name": "@我的组织/类型",
  "version": "1.0.0",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./用户": {
      "types": "./dist/用户.d.ts",
      "default": "./dist/用户.js"
    }
  }
}

配置共享

跨包共享构建和工具配置。

// 包/tsconfig/基础.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  }
}
// 应用/网页/tsconfig.json
{
  "extends": "@我的组织/tsconfig/基础.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src"],
  "references": [
    { "path": "../../包/用户界面" },
    { "path": "../../包/工具" }
  ]
}

构建依赖

依赖图

理解和可视化包依赖关系。

{
  "name": "@我的组织/网页",
  "dependencies": {
    "@我的组织/用户界面": "workspace:*",
    "@我的组织/API客户端": "workspace:*"
  }
}

生成依赖图:

# 使用pnpm
pnpm list --depth 10 --json > 依赖.json

# 使用Nx
nx graph

# 使用自定义脚本
node 脚本/生成-依赖图.js

构建顺序优化

确保包按正确的依赖顺序构建。

{
  "name": "我的单仓库",
  "scripts": {
    "构建": "turbo run 构建",
    "构建:顺序": "pnpm -r --workspace-concurrency=1 run 构建"
  }
}

Turbo管道配置:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "构建": {
      "dependsOn": ["^构建"],
      "outputs": ["dist/**", ".next/**"]
    },
    "测试": {
      "dependsOn": ["构建"],
      "outputs": ["coverage/**"]
    }
  }
}

依赖解析

  • ^构建 - 先构建依赖
  • dependsOn - 显式任务依赖
  • 拓扑排序以确保正确顺序
  • 安全时并行执行

循环依赖检测

预防和检测循环依赖。

// 脚本/检查-循环依赖.js
import madge from 'madge';

async function 检查循环依赖() {
  const result = await madge('src', {
    fileExtensions: ['ts', 'tsx'],
    detectiveOptions: {
      ts: { skipTypeImports: true }
    }
  });

  const circular = result.circular();
  if (circular.length > 0) {
    console.error('检测到循环依赖:');
    circular.forEach(cycle => {
      console.error(cycle.join(' -> '));
    });
    process.exit(1);
  }
}

检查循环依赖();

添加到CI管道:

# .github/workflows/ci.yml
- name: 检查循环依赖
  run: pnpm 检查:循环

版本控制策略

独立版本控制

每个包有自己的版本,独立发布。

{
  "name": "@我的组织/用户界面",
  "version": "2.1.0"
}
{
  "name": "@我的组织/工具",
  "version": "1.5.3"
}

优势

  • 细粒度版本控制
  • 独立发布周期
  • 清晰的包成熟度
  • 每个包的语义版本控制

适用场景

  • 包有不同的稳定性级别
  • 发布频率差异显著
  • 包用于不同目的

固定/锁定版本控制

所有包共享相同的版本号。

{
  "name": "@我的组织/用户界面",
  "version": "3.2.0"
}
{
  "name": "@我的组织/工具",
  "version": "3.2.0"
}

优势

  • 简化版本管理
  • 清晰的发布协调
  • 更容易跟踪兼容性
  • 统一的变更日志

适用场景

  • 包紧密耦合
  • 所有包一起发布
  • 单个产品有多个包
  • 版本同步是关键

单仓库中的语义版本控制

对工作空间包应用语义版本控制原则。

重大变更(主要):

  • 更改公共API签名
  • 移除导出函数
  • 显著更改函数行为
  • 更新有重大变更的对等依赖

新功能(次要):

  • 添加新导出
  • 添加可选参数
  • 增强现有功能
  • 添加新可选功能

错误修复(补丁):

  • 修复错误而不更改API
  • 更新文档
  • 重构内部实现
  • 更新依赖(非重大变更)

最佳实践

1. 清晰的包边界

为每个包定义明确的边界和职责。

实现

  • 记录包的用途和范围
  • 明确定义公共API
  • 使用桶导出(index.ts
  • 最小化跨包耦合
  • 定期审查包边界

示例

// 包/用户界面/源代码/索引.ts - 清晰的公共API
export { 按钮 } from './组件/按钮';
export { 输入 } from './组件/输入';
export type { 按钮属性, 输入属性 } from './类型';

// 不导出内部实现细节
// ./组件/按钮/按钮样式.ts
// ./工具/内部辅助工具.ts

2. 最小化包间耦合

减少包间的依赖以保持灵活性。

实现

  • 使用依赖注入
  • 优先组合而非继承
  • 定义清晰的接口
  • 避免深度依赖链
  • 使用事件/消息传递实现松散耦合

示例

// 通过接口实现松散耦合
interface 日志记录器 {
  日志(消息: 字符串): void;
}

class 支付服务 {
  constructor(private 日志记录器: 日志记录器) {}

  处理支付(金额: number) {
    this.日志记录器.日志(`处理支付: ${金额}`);
    // 实现
  }
}

3. 共享工具配置

在所有包中维护一致的工具配置。

实现

  • 创建共享配置包
  • 扩展基础配置
  • 使用工作空间继承
  • 记录配置决策
  • 自动化配置验证

示例

// 包/eslint配置/索引.json
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "no-console": "warn",
    "@typescript-eslint/no-unused-vars": "error"
  }
}

4. 一致的命名约定

跨包使用可预测的命名模式。

实现

  • 为所有包添加作用域(@组织/包名称
  • 使用短横线命名法命名包
  • 为相关包使用一致的前缀
  • 遵循语言/生态系统约定
  • 记录命名标准

示例

@我的组织/网页应用
@我的组织/移动端应用
@我的组织/用户界面组件
@我的组织/API客户端
@我的组织/工具日期
@我的组织/工具字符串
@我的组织/配置eslint
@我的组织/配置typescript

5. 文档标准

维护所有包的全面文档。

实现

  • 每个包都有的README
  • API文档
  • 使用示例
  • 迁移指南
  • 贡献指南

示例

# @我的组织/用户界面

MyOrg应用的React组件库。

## 安装

`pnpm add @我的组织/用户界面`

## 使用

import { 按钮 } from '@我的组织/用户界面';

<按钮 onClick={处理点击}>点击我</按钮>

## API参考

查看[API.md](./API.md)获取详细文档。

6. 包所有权

为包分配明确的所有权和责任。

实现

  • CODEOWNERS文件
  • 包维护者文档
  • 变更审查流程
  • 沟通渠道
  • 所有权轮换计划

示例

# CODEOWNERS
/包/用户界面/ @前端团队
/包/API客户端/ @API团队
/包/身份验证/ @安全团队
/服务/ @后端团队

7. API合约

定义和维护包间清晰的API合约。

实现

  • TypeScript接口
  • OpenAPI规范
  • JSON模式
  • 合约测试
  • 版本兼容性矩阵

示例

// 包/API客户端/源代码/合约.ts
/**
 * 用户API合约
 * @version 1.0.0
 */
export interface 用户API {
  获取用户(id: 字符串): Promise<用户>;
  创建用户(data: 创建用户输入): Promise<用户>;
  更新用户(id: 字符串, data: 更新用户输入): Promise<用户>;
  删除用户(id: 字符串): Promise<void>;
}

8. 迁移策略

计划包更新和重大变更。

实现

  • 弃用警告
  • 迁移指南
  • 兼容层
  • 自动化迁移(代码修改工具)
  • 版本升级路径

示例

// 弃用并提供迁移路径
/**
 * @deprecated 使用 `获取用户` 代替
 * 此函数将在v3.0.0中移除
 */
export function 获取用户旧(id: 字符串): Promise<用户> {
  console.warn('获取用户旧已被弃用,请使用获取用户代替');
  return 获取用户(id);
}

9. 安全边界

在包之间保持安全隔离。

实现

  • 分离敏感包
  • 访问控制策略
  • 按包进行安全扫描
  • 依赖审计
  • 秘密管理

示例

{
  "scripts": {
    "安全:审计": "pnpm audit --audit-level=high",
    "安全:检查": "pnpm dlx audit-ci --high"
  }
}

10. 测试隔离

确保测试没有意外的依赖。

实现

  • 包级测试配置
  • 模拟工作空间依赖
  • 集成测试套件
  • CI测试隔离
  • 测试数据管理

示例

// 包/用户界面/源代码/__测试__/按钮.测试.tsx
import { render, screen } from '@testing-library/react';
import { 按钮 } from '../按钮';

// 测试独立于其他包
describe('按钮', () => {
  it('正确渲染', () => {
    render(<按钮>点击我</按钮>);
    expect(screen.getByText('点击我')).toBeInTheDocument();
  });
});

常见陷阱

1. 包间紧密耦合

在包之间创建过多的依赖关系。

症状

  • 变更需要更新多个包
  • 难以提取或移动包
  • 长依赖链
  • 循环依赖

解决方案

  • 使用依赖注入
  • 定义清晰接口
  • 实现事件驱动通信
  • 定期重构以减少耦合

2. 包职责不明确

包的用途重叠或未定义。

症状

  • 功能重复
  • 不确定在哪里添加功能
  • 实现不一致
  • 代码重复

解决方案

  • 清晰记录包的用途
  • 单一职责原则
  • 定期架构审查
  • 重构以澄清边界

3. 不一致的依赖版本

不同包中使用相同依赖的不同版本。

症状

  • 构建错误
  • 运行时冲突
  • 对等依赖警告
  • 捆绑包大小膨胀

解决方案

  • 使用工作空间协议
  • 实现syncpack或类似工具
  • 集中版本管理
  • CI检查一致性

4. 构建优化差

未充分利用缓存和增量构建。

症状

  • 构建时间慢
  • 重新构建未更改的包
  • CI管道运行时间长
  • 开发者挫败感

解决方案

  • 实现构建缓存(Turborepo, Nx)
  • 使用受影响分析
  • 配置增量构建
  • 优化构建管道

5. 缺少文档

包和API的文档不足。

症状

  • 频繁询问用法问题
  • 错误的用法模式
  • 困难的上手
  • 知识孤岛

解决方案

  • 每个包都有README
  • API文档生成
  • 使用示例
  • 上手指南

6. 单仓库中的整体思维

将单仓库视为单一大型应用程序。

症状

  • 共享全局状态
  • 代码紧密耦合
  • 难以提取包
  • 无清晰的包边界

解决方案

  • 设计包作为独立单元
  • 最小化共享全局状态
  • 清晰的关注点分离
  • 定期边界审查

7. 过度共享代码

共享应保持私有的代码。

症状

  • 暴露实现细节
  • 脆弱的依赖关系
  • 难以重构
  • 版本管理复杂性

解决方案

  • 使用桶导出作为公共API
  • 保持内部私有
  • 记录公共与私有
  • 定期审查导出

8. 忽略包边界

从包内部导入而不是公共API。

症状

  • 脆弱的导入
  • 重构时的重大变更
  • 不清晰的依赖关系
  • 类型错误

解决方案

  • 仅从包根目录导入
  • 配置代码检查规则
  • 使用TypeScript项目引用
  • 代码审查强制执行

9. 无版本控制策略

缺乏清晰的版本控制方法。

症状

  • 兼容性不明确
  • 无警告的重大变更
  • 困难的回滚
  • 消费者困惑

解决方案

  • 选择固定或独立版本控制
  • 使用changesets或约定提交
  • 语义版本控制纪律
  • 自动化版本管理

10. 复杂依赖链

深度或循环依赖关系。

症状

  • 难以理解流程
  • 构建顺序问题
  • 循环依赖错误
  • 维护困难

解决方案

  • 可视化依赖图
  • 重构以减少深度
  • 打破循环依赖
  • 使用依赖注入

何时使用此技能

在以下情况下应用单仓库架构原则:

  • 设计新的单仓库 - 设置结构和组织
  • 重构现有仓库 - 改进架构和组织
  • 迁移到单仓库 - 从多仓库移动到单仓库
  • 扩展单仓库 - 从小型增长到大型单仓库
  • 组织包 - 决定包边界和结构
  • 管理依赖 - 处理内部和外部依赖
  • 建立模式 - 创建架构标准
  • 审查架构 - 评估现有单仓库结构
  • 故障排除问题 - 解决依赖或组织问题
  • 计划重构 - 重构包或依赖关系

资源