monorepo-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMonorepo Architecture Skill
Monorepo架构技能
Overview
概述
This skill provides comprehensive guidance on designing and structuring
monorepos, including workspace organization, dependency management, versioning
strategies, and architectural patterns that scale from small projects to
enterprise applications.
本技能提供关于设计和构建monorepo的全面指导,包括工作区组织、依赖管理、版本控制策略,以及从小型项目到企业级应用都适用的可扩展架构模式。
Monorepo vs Polyrepo
Monorepo vs Polyrepo
When to Choose Monorepo
何时选择Monorepo
A monorepo is beneficial when:
- Code sharing is frequent: Multiple projects share common libraries, utilities, or components
- Atomic changes needed: Changes span multiple packages and need to be deployed together
- Unified tooling: All projects benefit from consistent linting, testing, and build processes
- Team collaboration: Teams work across project boundaries and need visibility into related code
- Version synchronization: Related packages should maintain version alignment
- Refactoring at scale: Large-scale refactoring across projects is common
- Single source of truth: All code, documentation, and tooling in one place
当满足以下条件时,Monorepo更为适用:
- 代码共享频繁:多个项目共享通用库、工具或组件
- 需要原子变更:变更涉及多个包且需一起部署
- 统一工具链:所有项目受益于一致的代码检查、测试和构建流程
- 团队协作需求:团队跨项目协作,需要查看相关代码
- 版本同步:相关包需保持版本一致
- 大规模重构:跨项目的大规模重构较为常见
- 单一事实源:所有代码、文档和工具链集中在一处
When to Choose Polyrepo
何时选择Polyrepo
A polyrepo is beneficial when:
- Independent release cycles: Projects deploy on completely different schedules
- Different tech stacks: Projects use incompatible tooling or languages
- Access control: Different teams need isolated access to separate codebases
- Repository size concerns: Combined codebase would be too large to manage efficiently
- External dependencies: Projects are maintained by different organizations
- Simple project structure: Overhead of monorepo tooling outweighs benefits
当满足以下条件时,Polyrepo更为适用:
- 独立发布周期:项目部署时间表完全不同
- 不同技术栈:项目使用不兼容的工具或语言
- 访问控制需求:不同团队需要对独立代码库的隔离访问权限
- 仓库大小顾虑:合并后的代码库过大难以高效管理
- 外部依赖:项目由不同组织维护
- 简单项目结构:Monorepo工具链的开销超过其收益
Tradeoffs
权衡对比
Monorepo Advantages:
- Simplified dependency management
- Easier refactoring across boundaries
- Consistent tooling and standards
- Better code discoverability
- Atomic commits across projects
- Single CI/CD pipeline
Monorepo Challenges:
- Repository size growth
- CI/CD complexity
- Build time management
- Git performance at scale
- Tooling requirements
- Learning curve for developers
Monorepo优势:
- 简化依赖管理
- 跨边界重构更轻松
- 一致的工具链和标准
- 更好的代码可发现性
- 跨项目的原子提交
- 单一CI/CD流水线
Monorepo挑战:
- 仓库规模增长
- CI/CD复杂度提升
- 构建时间管理难度大
- 大规模下Git性能问题
- 对工具链的要求高
- 开发者学习曲线陡峭
Repository Structure Patterns
仓库结构模式
Package-Based Structure
基于包的结构
Organize by technical layer or package type.
text
my-monorepo/
├── apps/
│ ├── web/ # Next.js web application
│ ├── mobile/ # React Native app
│ └── api/ # Node.js API server
├── packages/
│ ├── ui/ # Shared UI components
│ ├── utils/ # Shared utilities
│ ├── config/ # Shared configurations
│ └── types/ # Shared TypeScript types
├── services/
│ ├── auth/ # Authentication service
│ ├── payments/ # Payment processing
│ └── notifications/ # Notification service
└── tooling/
├── eslint-config/ # ESLint configuration
└── tsconfig/ # TypeScript configurationBest for: Technical separation, shared library focus, platform diversity.
按技术层或包类型组织。
text
my-monorepo/
├── apps/
│ ├── web/ # Next.js web application
│ ├── mobile/ # React Native app
│ └── api/ # Node.js API server
├── packages/
│ ├── ui/ # Shared UI components
│ ├── utils/ # Shared utilities
│ ├── config/ # Shared configurations
│ └── types/ # Shared TypeScript types
├── services/
│ ├── auth/ # Authentication service
│ ├── payments/ # Payment processing
│ └── notifications/ # Notification service
└── tooling/
├── eslint-config/ # ESLint configuration
└── tsconfig/ # TypeScript configuration最适用场景: 技术分层、共享库聚焦、平台多样性。
Domain-Based Structure
基于领域的结构
Organize by business domain or feature.
text
my-monorepo/
├── domains/
│ ├── user/
│ │ ├── api/ # User API
│ │ ├── web/ # User web UI
│ │ ├── mobile/ # User mobile UI
│ │ └── shared/ # User shared code
│ ├── billing/
│ │ ├── api/
│ │ ├── web/
│ │ └── shared/
│ └── analytics/
│ ├── api/
│ ├── web/
│ └── shared/
└── shared/
├── ui/ # Cross-domain UI components
├── utils/ # Cross-domain utilities
└── config/ # Cross-domain configBest for: Domain-driven design, team ownership by feature, microservices
architecture.
按业务领域或功能组织。
text
my-monorepo/
├── domains/
│ ├── user/
│ │ ├── api/ # User API
│ │ ├── web/ # User web UI
│ │ ├── mobile/ # User mobile UI
│ │ └── shared/ # User shared code
│ ├── billing/
│ │ ├── api/
│ │ ├── web/
│ │ └── shared/
│ └── analytics/
│ ├── api/
│ ├── web/
│ └── shared/
└── shared/
├── ui/ # Cross-domain UI components
├── utils/ # Cross-domain utilities
└── config/ # Cross-domain config最适用场景: 领域驱动设计、按功能划分团队所有权、微服务架构。
Hybrid Structure
混合结构
Combine package-based and domain-based approaches.
text
my-monorepo/
├── apps/
│ ├── customer-portal/ # Customer-facing app
│ └── admin-dashboard/ # Admin app
├── features/
│ ├── auth/ # Authentication feature
│ ├── checkout/ # Checkout feature
│ └── inventory/ # Inventory feature
├── packages/
│ ├── ui/ # Shared UI library
│ ├── api-client/ # API client library
│ └── analytics/ # Analytics library
└── infrastructure/
├── database/ # Database utilities
├── messaging/ # Message queue
└── deployment/ # Deployment configsBest for: Complex organizations, balancing technical and domain concerns.
结合基于包和基于领域的方法。
text
my-monorepo/
├── apps/
│ ├── customer-portal/ # Customer-facing app
│ └── admin-dashboard/ # Admin app
├── features/
│ ├── auth/ # Authentication feature
│ ├── checkout/ # Checkout feature
│ └── inventory/ # Inventory feature
├── packages/
│ ├── ui/ # Shared UI library
│ ├── api-client/ # API client library
│ └── analytics/ # Analytics library
└── infrastructure/
├── database/ # Database utilities
├── messaging/ # Message queue
└── deployment/ # Deployment configs最适用场景: 复杂组织,平衡技术与领域关注点。
Workspace Configuration
工作区配置
NPM Workspaces
NPM Workspaces
Basic workspace setup using native NPM workspaces.
json
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dev": "npm run dev --workspaces",
"build": "npm run build --workspaces",
"test": "npm run test --workspaces"
},
"devDependencies": {
"typescript": "^5.3.0",
"eslint": "^8.54.0"
}
}Key Features:
- Native NPM support (v7+)
- Simple configuration
- Automatic workspace linking
- Shared dependency hoisting
- Workspace-specific commands
使用原生NPM Workspaces的基础工作区设置。
json
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dev": "npm run dev --workspaces",
"build": "npm run build --workspaces",
"test": "npm run test --workspaces"
},
"devDependencies": {
"typescript": "^5.3.0",
"eslint": "^8.54.0"
}
}核心特性:
- 原生NPM支持(v7+)
- 配置简单
- 自动工作区链接
- 共享依赖提升
- 工作区专属命令
Yarn Workspaces
Yarn Workspaces
Yarn's workspace implementation with additional features.
json
{
"name": "my-monorepo",
"private": true,
"workspaces": {
"packages": [
"apps/*",
"packages/*"
],
"nohoist": [
"**/react-native",
"**/react-native/**"
]
},
"packageManager": "yarn@3.6.4"
}With for Yarn Berry:
.yarnrc.ymlyaml
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
enableGlobalCache: true
compressionLevel: mixedKey Features:
- Fast installation
- Plugin system (Yarn Berry)
- Advanced workspace commands
- No-hoist for specific dependencies
- Zero-installs capability
Yarn的工作区实现,具备额外功能。
json
{
"name": "my-monorepo",
"private": true,
"workspaces": {
"packages": [
"apps/*",
"packages/*"
],
"nohoist": [
"**/react-native",
"**/react-native/**"
]
},
"packageManager": "yarn@3.6.4"
}配合Yarn Berry的:
.yarnrc.ymlyaml
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
enableGlobalCache: true
compressionLevel: mixed核心特性:
- 安装速度快
- 插件系统(Yarn Berry)
- 高级工作区命令
- 特定依赖不提升(No-hoist)
- 零安装能力
PNPM Workspaces
PNPM Workspaces
PNPM's efficient workspace implementation.
yaml
undefinedPNPM的高效工作区实现。
yaml
undefinedpnpm-workspace.yaml
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
- 'services/*'
- '!/test/'
With workspace-specific `.npmrc`:
```inipackages:
- 'apps/*'
- 'packages/*'
- 'services/*'
- '!/test/'
配合工作区专属的`.npmrc`:
```ini.npmrc
.npmrc
shared-workspace-lockfile=true
link-workspace-packages=true
prefer-workspace-packages=true
strict-peer-dependencies=false
auto-install-peers=true
**Key Features**:
- Content-addressable storage
- Strict node_modules structure
- Faster than NPM/Yarn
- Efficient disk space usage
- Built-in monorepo supportshared-workspace-lockfile=true
link-workspace-packages=true
prefer-workspace-packages=true
strict-peer-dependencies=false
auto-install-peers=true
**核心特性**:
- 内容可寻址存储
- 严格的node_modules结构
- 比NPM/Yarn更快
- 高效的磁盘空间利用
- 内置monorepo支持Cargo Workspaces (Rust)
Cargo Workspaces (Rust)
Rust monorepo workspace configuration.
toml
undefinedRust monorepo工作区配置。
toml
undefinedCargo.toml (root)
Cargo.toml (root)
[workspace]
members = [
"crates/core",
"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"] }
Individual crate:
```toml[workspace]
members = [
"crates/core",
"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配置:
```tomlcrates/core/Cargo.toml
crates/core/Cargo.toml
[package]
name = "my-core"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
serde.workspace = true
tokio.workspace = true
my-api = { path = "../api" }
undefined[package]
name = "my-core"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
serde.workspace = true
tokio.workspace = true
my-api = { path = "../api" }
undefinedDependency Management
依赖管理
Internal Package Dependencies
内部包依赖
Specify dependencies on other workspace packages.
json
{
"name": "@myorg/web-app",
"version": "1.0.0",
"dependencies": {
"@myorg/ui": "workspace:*",
"@myorg/utils": "workspace:^",
"@myorg/api-client": "1.2.3",
"react": "^18.2.0"
}
}Workspace Protocol Variants:
- - Any version in workspace
workspace:* - - Compatible version (semver caret)
workspace:^ - - Patch-level version (semver tilde)
workspace:~ - Specific version - Exact workspace version
指定对其他工作区包的依赖。
json
{
"name": "@myorg/web-app",
"version": "1.0.0",
"dependencies": {
"@myorg/ui": "workspace:*",
"@myorg/utils": "workspace:^",
"@myorg/api-client": "1.2.3",
"react": "^18.2.0"
}
}工作区协议变体:
- - 工作区内的任意版本
workspace:* - - 兼容版本(Semver脱字符)
workspace:^ - - 补丁级版本(Semver波浪号)
workspace:~ - 特定版本 - 精确的工作区版本
Shared Dependencies Hoisting
共享依赖提升
Configure how dependencies are hoisted to root.
json
{
"name": "my-monorepo",
"workspaces": {
"packages": ["packages/*"],
"nohoist": [
"**/react-native",
"**/react-native/**",
"**/@babel/**"
]
}
}Hoisting Strategies:
- Full hoisting: All common dependencies at root (default)
- Selective hoisting: Specific packages hoisted, others isolated
- No hoisting: Each package has isolated dependencies
- Public hoisting: Only public dependencies hoisted
配置如何将依赖提升到根目录。
json
{
"name": "my-monorepo",
"workspaces": {
"packages": ["packages/*"],
"nohoist": [
"**/react-native",
"**/react-native/**",
"**/@babel/**"
]
}
}提升策略:
- 完全提升: 所有公共依赖都在根目录(默认)
- 选择性提升: 特定包被提升,其他包隔离
- 不提升: 每个包都有独立的依赖
- 公共提升: 仅公共依赖被提升
Version Synchronization
版本同步
Keep related dependencies in sync across packages.
json
{
"name": "my-monorepo",
"private": true,
"syncpack": {
"semverGroups": [
{
"range": "",
"dependencies": ["react", "react-dom"],
"packages": ["**"]
}
],
"versionGroups": [
{
"label": "React ecosystem must match",
"dependencies": ["react", "react-dom"],
"dependencyTypes": ["prod", "dev"],
"pinVersion": "18.2.0"
}
]
}
}Use tools like to enforce consistency:
syncpackbash
undefined保持相关依赖在所有包中的版本一致。
json
{
"name": "my-monorepo",
"private": true,
"syncpack": {
"semverGroups": [
{
"range": "",
"dependencies": ["react", "react-dom"],
"packages": ["**"]
}
],
"versionGroups": [
{
"label": "React ecosystem must match",
"dependencies": ["react", "react-dom"],
"dependencyTypes": ["prod", "dev"],
"pinVersion": "18.2.0"
}
]
}
}使用等工具强制一致性:
syncpackbash
undefinedCheck for version inconsistencies
检查版本不一致
pnpm syncpack list-mismatches
pnpm syncpack list-mismatches
Fix version inconsistencies
修复版本不一致
pnpm syncpack fix-mismatches
pnpm syncpack fix-mismatches
Update all versions
更新所有版本
pnpm syncpack update
undefinedpnpm syncpack update
undefinedPeer Dependencies in Monorepos
Monorepo中的对等依赖
Handle peer dependencies correctly across workspace packages.
json
{
"name": "@myorg/ui",
"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"
}
}Best Practices:
- Declare peer dependencies in shared libraries
- Include peer deps as dev dependencies for testing
- Use for optional peers
peerDependenciesMeta - Document peer dependency requirements
- Test with minimum and maximum peer versions
正确处理工作区包之间的对等依赖。
json
{
"name": "@myorg/ui",
"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 - 记录对等依赖要求
- 使用最小和最大对等版本进行测试
Code Organization
代码组织
Shared Code Patterns
共享代码模式
Organize shared code for maximum reusability.
text
packages/
├── ui/
│ ├── src/
│ │ ├── components/ # Reusable components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── styles/ # Shared styles
│ │ └── index.ts # Public API
│ └── package.json
├── utils/
│ ├── src/
│ │ ├── string/ # String utilities
│ │ ├── date/ # Date utilities
│ │ ├── validation/ # Validation functions
│ │ └── index.ts # Public API
│ └── package.json
└── config/
├── eslint-config/
├── tsconfig/
└── prettier-config/Shared Library Design:
- Clear public API via barrel exports
- Minimal external dependencies
- Well-documented interfaces
- Comprehensive unit tests
- Semantic versioning
- Changelog maintenance
组织共享代码以实现最大复用性。
text
packages/
├── ui/
│ ├── src/
│ │ ├── components/ # 可复用组件
│ │ ├── hooks/ # 自定义React hooks
│ │ ├── styles/ # 共享样式
│ │ └── index.ts # 公共API
│ └── package.json
├── utils/
│ ├── src/
│ │ ├── string/ # 字符串工具
│ │ ├── date/ # 日期工具
│ │ ├── validation/ # 验证函数
│ │ └── index.ts # 公共API
│ └── package.json
└── config/
├── eslint-config/
├── tsconfig/
└── prettier-config/共享库设计:
- 通过桶导出(barrel exports)明确公共API
- 最小化外部依赖
- 接口文档完善
- 全面的单元测试
- 语义化版本控制
- 维护变更日志
Type Sharing Across Packages
跨包类型共享
Share TypeScript types efficiently.
typescript
// packages/types/src/user.ts
export interface User {
id: string;
email: string;
name: string;
role: UserRole;
}
export enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest',
}
export type CreateUserInput = Omit<User, 'id'>;
export type UpdateUserInput = Partial<CreateUserInput>;json
// packages/types/package.json
{
"name": "@myorg/types",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./user": {
"types": "./dist/user.d.ts",
"default": "./dist/user.js"
}
}
}高效共享TypeScript类型。
typescript
// packages/types/src/user.ts
export interface User {
id: string;
email: string;
name: string;
role: UserRole;
}
export enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest',
}
export type CreateUserInput = Omit<User, 'id'>;
export type UpdateUserInput = Partial<CreateUserInput>;json
// packages/types/package.json
{
"name": "@myorg/types",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./user": {
"types": "./dist/user.d.ts",
"default": "./dist/user.js"
}
}
}Configuration Sharing
配置共享
Share build and tooling configuration across packages.
javascript
// packages/tsconfig/base.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
}
}json
// apps/web/tsconfig.json
{
"extends": "@myorg/tsconfig/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [
{ "path": "../../packages/ui" },
{ "path": "../../packages/utils" }
]
}跨包共享构建和工具链配置。
javascript
// packages/tsconfig/base.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
}
}json
// apps/web/tsconfig.json
{
"extends": "@myorg/tsconfig/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"references": [
{ "path": "../../packages/ui" },
{ "path": "../../packages/utils" }
]
}Build Dependencies
构建依赖
Dependency Graphs
依赖图
Understand and visualize package dependencies.
json
{
"name": "@myorg/web",
"dependencies": {
"@myorg/ui": "workspace:*",
"@myorg/api-client": "workspace:*"
}
}Generate dependency graph:
bash
undefined理解并可视化包依赖关系。
json
{
"name": "@myorg/web",
"dependencies": {
"@myorg/ui": "workspace:*",
"@myorg/api-client": "workspace:*"
}
}生成依赖图:
bash
undefinedUsing pnpm
使用pnpm
pnpm list --depth 10 --json > deps.json
pnpm list --depth 10 --json > deps.json
Using Nx
使用Nx
nx graph
nx graph
Using custom script
使用自定义脚本
node scripts/generate-dep-graph.js
undefinednode scripts/generate-dep-graph.js
undefinedBuild Order Optimization
构建顺序优化
Ensure packages build in correct dependency order.
json
{
"name": "my-monorepo",
"scripts": {
"build": "turbo run build",
"build:order": "pnpm -r --workspace-concurrency=1 run build"
}
}Turbo pipeline configuration:
json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}Dependency Resolution:
- - Build dependencies first
^build - - Explicit task dependencies
dependsOn - Topological sorting for correct order
- Parallel execution when safe
确保包按正确的依赖顺序构建。
json
{
"name": "my-monorepo",
"scripts": {
"build": "turbo run build",
"build:order": "pnpm -r --workspace-concurrency=1 run build"
}
}Turbo流水线配置:
json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}依赖解析:
- - 先构建依赖包
^build - - 明确的任务依赖
dependsOn - 拓扑排序确保正确顺序
- 安全时并行执行
Circular Dependency Detection
循环依赖检测
Prevent and detect circular dependencies.
javascript
// scripts/check-circular-deps.js
import madge from 'madge';
async function checkCircularDeps() {
const result = await madge('src', {
fileExtensions: ['ts', 'tsx'],
detectiveOptions: {
ts: { skipTypeImports: true }
}
});
const circular = result.circular();
if (circular.length > 0) {
console.error('Circular dependencies detected:');
circular.forEach(cycle => {
console.error(cycle.join(' -> '));
});
process.exit(1);
}
}
checkCircularDeps();Add to CI pipeline:
yaml
undefined预防和检测循环依赖。
javascript
// scripts/check-circular-deps.js
import madge from 'madge';
async function checkCircularDeps() {
const result = await madge('src', {
fileExtensions: ['ts', 'tsx'],
detectiveOptions: {
ts: { skipTypeImports: true }
}
});
const circular = result.circular();
if (circular.length > 0) {
console.error('Circular dependencies detected:');
circular.forEach(cycle => {
console.error(cycle.join(' -> '));
});
process.exit(1);
}
}
checkCircularDeps();添加到CI流水线:
yaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
- name: Check circular dependencies run: pnpm check:circular
undefined- name: Check circular dependencies run: pnpm check:circular
undefinedVersioning Strategies
版本控制策略
Independent Versioning
独立版本控制
Each package has its own version, released independently.
json
{
"name": "@myorg/ui",
"version": "2.1.0"
}json
{
"name": "@myorg/utils",
"version": "1.5.3"
}Advantages:
- Fine-grained version control
- Independent release cycles
- Clear package maturity
- Semantic versioning per package
Use when:
- Packages have different stability levels
- Release frequency varies significantly
- Packages serve different purposes
每个包有自己的版本,独立发布。
json
{
"name": "@myorg/ui",
"version": "2.1.0"
}json
{
"name": "@myorg/utils",
"version": "1.5.3"
}优势:
- 细粒度版本控制
- 独立发布周期
- 明确的包成熟度
- 每个包遵循语义化版本
适用场景:
- 包的稳定性水平不同
- 发布频率差异显著
- 包的用途不同
Fixed/Locked Versioning
固定/锁定版本控制
All packages share the same version number.
json
{
"name": "@myorg/ui",
"version": "3.2.0"
}json
{
"name": "@myorg/utils",
"version": "3.2.0"
}Advantages:
- Simplified version management
- Clear release coordination
- Easier to track compatibility
- Unified changelog
Use when:
- Packages are tightly coupled
- All packages release together
- Single product with multiple packages
- Version sync is critical
所有包共享相同的版本号。
json
{
"name": "@myorg/ui",
"version": "3.2.0"
}json
{
"name": "@myorg/utils",
"version": "3.2.0"
}优势:
- 简化版本管理
- 清晰的发布协调
- 更容易跟踪兼容性
- 统一的变更日志
适用场景:
- 包之间耦合紧密
- 所有包一起发布
- 单一产品包含多个包
- 版本同步至关重要
Semantic Versioning in Monorepos
Monorepo中的语义化版本控制
Apply semver principles to workspace packages.
Breaking Changes (Major):
- Change public API signatures
- Remove exported functions
- Change function behavior significantly
- Update peer dependencies with breaking changes
New Features (Minor):
- Add new exports
- Add optional parameters
- Enhance existing functionality
- Add new optional features
Bug Fixes (Patch):
- Fix bugs without API changes
- Update documentation
- Refactor internal implementation
- Update dependencies (non-breaking)
将语义化版本原则应用于工作区包。
破坏性变更(主版本):
- 变更公共API签名
- 删除导出函数
- 显著改变函数行为
- 更新带有破坏性变更的对等依赖
新功能(次版本):
- 添加新的导出
- 添加可选参数
- 增强现有功能
- 添加新的可选特性
Bug修复(补丁版本):
- 修复Bug且不改变API
- 更新文档
- 重构内部实现
- 更新依赖(非破坏性)
Best Practices
最佳实践
1. Clear Package Boundaries
1. 清晰的包边界
Define explicit boundaries and responsibilities for each package.
Implementation:
- Document package purpose and scope
- Define public API explicitly
- Use barrel exports ()
index.ts - Minimize cross-package coupling
- Review package boundaries regularly
Example:
typescript
// packages/ui/src/index.ts - Clear public API
export { Button } from './components/Button';
export { Input } from './components/Input';
export type { ButtonProps, InputProps } from './types';
// Internal implementation details NOT exported
// ./components/Button/ButtonStyles.ts
// ./utils/internal-helper.ts为每个包定义明确的边界和职责。
实现方式:
- 记录包的用途和范围
- 明确定义公共API
- 使用桶导出(index.ts)
- 最小化跨包耦合
- 定期审查包边界
示例:
typescript
// packages/ui/src/index.ts - 清晰的公共API
export { Button } from './components/Button';
export { Input } from './components/Input';
export type { ButtonProps, InputProps } from './types';
// 内部实现细节不导出
// ./components/Button/ButtonStyles.ts
// ./utils/internal-helper.ts2. Minimal Coupling Between Packages
2. 包之间最小耦合
Reduce dependencies between packages to maintain flexibility.
Implementation:
- Use dependency injection
- Prefer composition over inheritance
- Define clear interfaces
- Avoid deep dependency chains
- Use events/messaging for loose coupling
Example:
typescript
// Loose coupling via interfaces
interface Logger {
log(message: string): void;
}
class PaymentService {
constructor(private logger: Logger) {}
processPayment(amount: number) {
this.logger.log(`Processing payment: ${amount}`);
// Implementation
}
}减少包之间的依赖以保持灵活性。
实现方式:
- 使用依赖注入
- 优先组合而非继承
- 定义清晰的接口
- 避免深层依赖链
- 使用事件/消息实现松耦合
示例:
typescript
// 通过接口实现松耦合
interface Logger {
log(message: string): void;
}
class PaymentService {
constructor(private logger: Logger) {}
processPayment(amount: number) {
this.logger.log(`Processing payment: ${amount}`);
// 实现逻辑
}
}3. Shared Tooling Configuration
3. 共享工具链配置
Maintain consistent tooling across all packages.
Implementation:
- Create shared config packages
- Extend base configurations
- Use workspace inheritance
- Document configuration decisions
- Automate configuration validation
Example:
json
// packages/eslint-config/index.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-console": "warn",
"@typescript-eslint/no-unused-vars": "error"
}
}在所有包中保持一致的工具链。
实现方式:
- 创建共享配置包
- 扩展基础配置
- 使用工作区继承
- 记录配置决策
- 自动化配置验证
示例:
json
// packages/eslint-config/index.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-console": "warn",
"@typescript-eslint/no-unused-vars": "error"
}
}4. Consistent Naming Conventions
4. 一致的命名约定
Use predictable naming patterns across packages.
Implementation:
- Scope all packages ()
@org/package-name - Use kebab-case for package names
- Prefix related packages consistently
- Follow language/ecosystem conventions
- Document naming standards
Example:
text
@myorg/web-app
@myorg/mobile-app
@myorg/ui-components
@myorg/api-client
@myorg/utils-date
@myorg/utils-string
@myorg/config-eslint
@myorg/config-typescript在所有包中使用可预测的命名模式。
实现方式:
- 为所有包添加作用域(@org/package-name)
- 包名使用短横线分隔(kebab-case)
- 相关包使用一致的前缀
- 遵循语言/生态系统约定
- 记录命名标准
示例:
text
@myorg/web-app
@myorg/mobile-app
@myorg/ui-components
@myorg/api-client
@myorg/utils-date
@myorg/utils-string
@myorg/config-eslint
@myorg/config-typescript5. Documentation Standards
5. 文档标准
Maintain comprehensive documentation for all packages.
Implementation:
- README in every package
- API documentation
- Usage examples
- Migration guides
- Contribution guidelines
Example:
markdown
undefined为所有包维护全面的文档。
实现方式:
- 每个包都有README
- API文档
- 使用示例
- 迁移指南
- 贡献指南
示例:
markdown
undefined@myorg/ui
@myorg/ui
React component library for MyOrg applications.
MyOrg应用的React组件库。
Installation
安装
pnpm add @myorg/uipnpm add @myorg/uiUsage
使用
import { Button } from '@myorg/ui';
<Button onClick={handleClick}>Click me</Button>
import { Button } from '@myorg/ui';
<Button onClick={handleClick}>Click me</Button>
API Reference
API参考
See API.md for detailed documentation.
undefined详见API.md获取详细文档。
undefined6. Package Ownership
6. 包所有权
Assign clear ownership and responsibility for packages.
Implementation:
- CODEOWNERS file
- Package maintainer documentation
- Review process for changes
- Communication channels
- Ownership rotation plan
Example:
text
undefined为包分配明确的所有权和职责。
实现方式:
- CODEOWNERS文件
- 包维护者文档
- 变更审查流程
- 沟通渠道
- 所有权轮换计划
示例:
text
undefinedCODEOWNERS
CODEOWNERS
/packages/ui/ @frontend-team
/packages/api-client/ @api-team
/packages/auth/ @security-team
/services/ @backend-team
undefined/packages/ui/ @frontend-team
/packages/api-client/ @api-team
/packages/auth/ @security-team
/services/ @backend-team
undefined7. API Contracts
7. API契约
Define and maintain clear API contracts between packages.
Implementation:
- TypeScript interfaces
- OpenAPI specifications
- JSON Schema
- Contract testing
- Version compatibility matrix
Example:
typescript
// packages/api-client/src/contracts.ts
/**
* User API contract
* @version 1.0.0
*/
export interface UserAPI {
getUser(id: string): Promise<User>;
createUser(data: CreateUserInput): Promise<User>;
updateUser(id: string, data: UpdateUserInput): Promise<User>;
deleteUser(id: string): Promise<void>;
}定义并维护包之间清晰的API契约。
实现方式:
- TypeScript接口
- OpenAPI规范
- JSON Schema
- 契约测试
- 版本兼容性矩阵
示例:
typescript
// packages/api-client/src/contracts.ts
/**
* 用户API契约
* @version 1.0.0
*/
export interface UserAPI {
getUser(id: string): Promise<User>;
createUser(data: CreateUserInput): Promise<User>;
updateUser(id: string, data: UpdateUserInput): Promise<User>;
deleteUser(id: string): Promise<void>;
}8. Migration Strategies
8. 迁移策略
Plan for package updates and breaking changes.
Implementation:
- Deprecation warnings
- Migration guides
- Compatibility layers
- Automated migrations (codemods)
- Version upgrade paths
Example:
typescript
// Deprecation with migration path
/**
* @deprecated Use `getUser` instead
* This function will be removed in v3.0.0
*/
export function fetchUser(id: string): Promise<User> {
console.warn('fetchUser is deprecated, use getUser instead');
return getUser(id);
}规划包更新和破坏性变更。
实现方式:
- 弃用警告
- 迁移指南
- 兼容层
- 自动化迁移(codemods)
- 版本升级路径
示例:
typescript
// 带有迁移路径的弃用提示
/**
* @deprecated 请使用`getUser`替代
* 该函数将在v3.0.0版本中移除
*/
export function fetchUser(id: string): Promise<User> {
console.warn('fetchUser已弃用,请使用getUser替代');
return getUser(id);
}9. Security Boundaries
9. 安全边界
Maintain security isolation between packages.
Implementation:
- Separate sensitive packages
- Access control policies
- Security scanning per package
- Dependency audit
- Secret management
Example:
json
{
"scripts": {
"security:audit": "pnpm audit --audit-level=high",
"security:check": "pnpm dlx audit-ci --high"
}
}在包之间维护安全隔离。
实现方式:
- 分离敏感包
- 访问控制策略
- 每个包的安全扫描
- 依赖审计
- 密钥管理
示例:
json
{
"scripts": {
"security:audit": "pnpm audit --audit-level=high",
"security:check": "pnpm dlx audit-ci --high"
}
}10. Testing Isolation
10. 测试隔离
Ensure tests don't have unintended dependencies.
Implementation:
- Package-level test configuration
- Mock workspace dependencies
- Integration test suites
- CI test isolation
- Test data management
Example:
typescript
// packages/ui/src/__tests__/Button.test.tsx
import { render, screen } from '@testing-library/react';
import { Button } from '../Button';
// Test isolated from other packages
describe('Button', () => {
it('renders correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});确保测试没有意外依赖。
实现方式:
- 包级测试配置
- 模拟工作区依赖
- 集成测试套件
- CI测试隔离
- 测试数据管理
示例:
typescript
// packages/ui/src/__tests__/Button.test.tsx
import { render, screen } from '@testing-library/react';
import { Button } from '../Button';
// 测试与其他包隔离
describe('Button', () => {
it('渲染正确', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});Common Pitfalls
常见陷阱
1. Tight Coupling Between Packages
1. 包之间过度耦合
Creating excessive dependencies between packages.
Symptoms:
- Changes require updates across many packages
- Difficult to extract or move packages
- Long dependency chains
- Circular dependencies
Solution:
- Use dependency injection
- Define clear interfaces
- Implement event-driven communication
- Regular refactoring to reduce coupling
在包之间创建过多依赖。
症状:
- 变更需要更新多个包
- 难以提取或移动包
- 长依赖链
- 循环依赖
解决方案:
- 使用依赖注入
- 定义清晰的接口
- 实现事件驱动通信
- 定期重构以减少耦合
2. Unclear Package Responsibilities
2. 包职责不清晰
Packages with overlapping or undefined purposes.
Symptoms:
- Duplicate functionality
- Uncertain where to add features
- Inconsistent implementations
- Code duplication
Solution:
- Document package purpose clearly
- Single Responsibility Principle
- Regular architecture reviews
- Refactor to clarify boundaries
包的用途重叠或不明确。
症状:
- 功能重复
- 不确定在哪里添加新功能
- 实现不一致
- 代码重复
解决方案:
- 清晰记录包的用途
- 遵循单一职责原则
- 定期进行架构审查
- 重构以明确边界
3. Inconsistent Dependency Versions
3. 依赖版本不一致
Different versions of same dependency across packages.
Symptoms:
- Build errors
- Runtime conflicts
- Peer dependency warnings
- Bundle size bloat
Solution:
- Use workspace protocol
- Implement syncpack or similar
- Centralize version management
- CI checks for consistency
不同包中同一依赖的版本不同。
症状:
- 构建错误
- 运行时冲突
- 对等依赖警告
- 包体积膨胀
解决方案:
- 使用工作区协议
- 实现syncpack或类似工具
- 集中版本管理
- CI检查一致性
4. Poor Build Optimization
4. 构建优化不足
Not leveraging caching and incremental builds.
Symptoms:
- Slow build times
- Rebuilding unchanged packages
- Long CI pipeline runs
- Developer frustration
Solution:
- Implement build caching (Turborepo, Nx)
- Use affected analysis
- Configure incremental builds
- Optimize build pipelines
未利用缓存和增量构建。
症状:
- 构建时间慢
- 重新构建未变更的包
- CI流水线运行时间长
- 开发者受挫
解决方案:
- 实现构建缓存(Turborepo、Nx)
- 使用受影响分析
- 配置增量构建
- 优化构建流水线
5. Missing Documentation
5. 文档缺失
Inadequate documentation for packages and APIs.
Symptoms:
- Frequent questions about usage
- Incorrect usage patterns
- Difficult onboarding
- Knowledge silos
Solution:
- README in every package
- API documentation generation
- Usage examples
- Onboarding guides
包和API的文档不足。
症状:
- 频繁询问使用方法
- 使用模式不正确
- 入职困难
- 知识孤岛
解决方案:
- 每个包都有README
- 生成API文档
- 使用示例
- 入职指南
6. Monolithic Thinking in Monorepo
6. 在Monorepo中采用单体思维
Treating monorepo as single large application.
Symptoms:
- Shared global state
- Tightly coupled code
- Difficult to extract packages
- No clear package boundaries
Solution:
- Design packages as independent units
- Minimize shared global state
- Clear separation of concerns
- Regular boundary reviews
将Monorepo视为单一大型应用。
症状:
- 共享全局状态
- 代码过度耦合
- 难以提取包
- 没有清晰的包边界
解决方案:
- 将包设计为独立单元
- 最小化共享全局状态
- 清晰的关注点分离
- 定期审查边界
7. Over-Sharing Code
7. 过度共享代码
Sharing code that should remain private.
Symptoms:
- Implementation details exposed
- Brittle dependencies
- Difficult to refactor
- Version management complexity
Solution:
- Use barrel exports for public API
- Keep internals private
- Document public vs private
- Review exports regularly
共享本应保持私有代码。
症状:
- 暴露实现细节
- 依赖脆弱
- 难以重构
- 版本管理复杂
解决方案:
- 使用桶导出暴露公共API
- 保持内部实现私有
- 记录公共与私有部分
- 定期审查导出内容
8. Ignoring Package Boundaries
8. 忽略包边界
Importing from package internals instead of public API.
Symptoms:
- Brittle imports
- Breaking changes on refactor
- Unclear dependencies
- Type errors
Solution:
- Only import from package root
- Configure linting rules
- Use TypeScript project references
- Code review enforcement
从包内部导入而非公共API。
症状:
- 脆弱的导入
- 重构时出现破坏性变更
- 依赖不清晰
- 类型错误
解决方案:
- 仅从包根目录导入
- 配置代码检查规则
- 使用TypeScript项目引用
- 代码审查强制执行
9. No Versioning Strategy
9. 无版本控制策略
Lack of clear versioning approach.
Symptoms:
- Unclear compatibility
- Breaking changes without warning
- Difficult rollbacks
- Consumer confusion
Solution:
- Choose fixed or independent versioning
- Use changesets or conventional commits
- Semantic versioning discipline
- Automated version management
缺乏清晰的版本控制方法。
症状:
- 兼容性不明确
- 破坏性变更无警告
- 回滚困难
- 使用者困惑
解决方案:
- 选择固定或独立版本控制
- 使用changesets或约定式提交
- 遵循语义化版本规范
- 自动化版本管理
10. Complex Dependency Chains
10. 复杂依赖链
Deep or circular dependency relationships.
Symptoms:
- Difficult to understand flow
- Build order issues
- Circular dependency errors
- Maintenance difficulty
Solution:
- Visualize dependency graph
- Refactor to reduce depth
- Break circular dependencies
- Use dependency injection
深层或循环的依赖关系。
症状:
- 难以理解流程
- 构建顺序问题
- 循环依赖错误
- 维护困难
解决方案:
- 可视化依赖图
- 重构以减少依赖深度
- 打破循环依赖
- 使用依赖注入
When to Use This Skill
何时使用本技能
Apply monorepo architecture principles when:
- Designing new monorepos - Setting up structure and organization
- Refactoring existing repos - Improving architecture and organization
- Migrating to monorepo - Moving from polyrepo to monorepo
- Scaling monorepo - Growing from small to large monorepo
- Organizing packages - Deciding package boundaries and structure
- Managing dependencies - Handling internal and external dependencies
- Establishing patterns - Creating architectural standards
- Reviewing architecture - Evaluating existing monorepo structure
- Troubleshooting issues - Solving dependency or organization problems
- Planning refactors - Restructuring packages or dependencies
在以下场景应用Monorepo架构原则:
- 设计新Monorepo - 设置结构和组织方式
- 重构现有仓库 - 改进架构和组织
- 迁移至Monorepo - 从Polyrepo迁移到Monorepo
- 扩展Monorepo - 从小型Monorepo扩展到大型
- 组织包 - 确定包边界和结构
- 管理依赖 - 处理内部和外部依赖
- 建立模式 - 创建架构标准
- 审查架构 - 评估现有Monorepo结构
- 排查问题 - 解决依赖或组织问题
- 规划重构 - 重构包或依赖
Resources
资源
- Monorepo.tools - Comprehensive comparison of monorepo tools and patterns
- Turborepo Handbook - Best practices for Turborepo architecture
- Nx Monorepo Guide - Architectural concepts and patterns
- PNPM Workspaces - Documentation for PNPM workspace features
- Yarn Workspaces - Yarn workspace implementation guide
- Lerna Documentation - Multi-package repository management
- Bazel Documentation - Build and test system for large monorepos
- Rush Documentation - Scalable monorepo manager for JavaScript
- Monorepo.tools - Monorepo工具和模式的全面对比
- Turborepo Handbook - Turborepo架构最佳实践
- Nx Monorepo Guide - 架构概念和模式
- PNPM Workspaces - PNPM工作区特性文档
- Yarn Workspaces - Yarn工作区实现指南
- Lerna Documentation - 多包仓库管理工具
- Bazel Documentation - 大型Monorepo的构建和测试系统
- Rush Documentation - 可扩展的JavaScript Monorepo管理器