Skip to content

Monorepo 架构分析

整体架构

Monorepo 采用工作空间(Workspace)架构,将多个包组织在一个仓库中。

┌─────────────────────────────────────────────┐
│              Monorepo Root                  │
├─────────────────────────────────────────────┤
│  1. packages/ (包目录)                      │
│     ├── package-a/                          │
│     ├── package-b/                          │
│     └── package-c/                          │
├─────────────────────────────────────────────┤
│  2. apps/ (应用目录)                        │
│     ├── app-a/                              │
│     └── app-b/                              │
├─────────────────────────────────────────────┤
│  3. tools/ (工具目录)                       │
│     ├── build/                              │
│     └── test/                               │
├─────────────────────────────────────────────┤
│  4. config/ (配置目录)                      │
│     ├── lerna.json                          │
│     ├── nx.json                             │
│     └── turbo.json                          │
└─────────────────────────────────────────────┘

核心组件

1. Workspace Manager

工作空间管理器负责管理所有工作空间。

职责:

  • 管理工作空间
  • 解析依赖关系
  • 协调构建流程

关键方法:

javascript
class WorkspaceManager {
  constructor(config) {
    this.config = config
    this.workspaces = new Map()
  }
  
  // 添加工作空间
  addWorkspace(workspace) {
    this.workspaces.set(workspace.name, workspace)
  }
  
  // 获取工作空间
  getWorkspace(name) {
    return this.workspaces.get(name)
  }
  
  // 获取所有工作空间
  getWorkspaces() {
    return Array.from(this.workspaces.values())
  }
  
  // 解析依赖关系
  resolveDependencies() {
    const graph = new DependencyGraph()
    
    for (const workspace of this.workspaces.values()) {
      graph.addNode(workspace.name, workspace)
      
      for (const dep of workspace.dependencies) {
        graph.addEdge(workspace.name, dep)
      }
    }
    
    return graph
  }
}

2. Dependency Graph

依赖图管理包之间的依赖关系。

职责:

  • 构建依赖图
  • 检测循环依赖
  • 计算构建顺序

关键方法:

javascript
class DependencyGraph {
  constructor() {
    this.nodes = new Map()
    this.edges = new Map()
  }
  
  // 添加节点
  addNode(name, workspace) {
    this.nodes.set(name, workspace)
    this.edges.set(name, new Set())
  }
  
  // 添加边
  addEdge(from, to) {
    if (this.edges.has(from)) {
      this.edges.get(from).add(to)
    }
  }
  
  // 获取依赖
  getDependencies(name) {
    return Array.from(this.edges.get(name) || [])
  }
  
  // 拓扑排序
  topologicalSort() {
    const visited = new Set()
    const result = []
    
    const visit = (name) => {
      if (visited.has(name)) return
      
      visited.add(name)
      
      for (const dep of this.getDependencies(name)) {
        visit(dep)
      }
      
      result.push(name)
    }
    
    for (const name of this.nodes.keys()) {
      visit(name)
    }
    
    return result.reverse()
  }
}

3. Build System

构建系统负责协调构建流程。

职责:

  • 并行构建
  • 增量构建
  • 缓存构建结果

关键方法:

javascript
class BuildSystem {
  constructor(config) {
    this.config = config
    this.cache = new Map()
  }
  
  // 构建工作空间
  async buildWorkspace(workspace) {
    const cacheKey = this.getCacheKey(workspace)
    
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey)
    }
    
    const result = await this.buildInternal(workspace)
    this.cache.set(cacheKey, result)
    
    return result
  }
  
  // 构建所有工作空间
  async buildAll(workspaces) {
    const graph = this.resolveDependencies(workspaces)
    const order = graph.topologicalSort()
    
    const results = []
    
    for (const name of order) {
      const workspace = graph.getWorkspace(name)
      const result = await this.buildWorkspace(workspace)
      results.push(result)
    }
    
    return results
  }
  
  // 并行构建
  async buildParallel(workspaces) {
    const promises = workspaces.map(ws => 
      this.buildWorkspace(ws)
    )
    
    return Promise.all(promises)
  }
}

依赖管理

依赖提升

将公共依赖提升到根目录:

packages/
  package-a/
    node_modules/
      lodash/  (未提升)
  package-b/
    node_modules/
      lodash/  (未提升)

提升后:
packages/
  node_modules/
    lodash/  (提升到根目录)
  package-a/
  package-b/

依赖去重

去重重复的依赖:

javascript
function dedupeDependencies(workspaces) {
  const versions = new Map()
  
  for (const workspace of workspaces) {
    for (const [name, version] of Object.entries(workspace.dependencies)) {
      if (versions.has(name)) {
        if (versions.get(name) !== version) {
          console.warn(`版本冲突: ${name}`)
        }
      } else {
        versions.set(name, version)
      }
    }
  }
  
  return versions
}

构建优化

1. 并行构建

javascript
async function buildParallel(workspaces) {
  const workers = os.cpus().length
  const chunks = chunkArray(workspaces, workers)
  
  const promises = chunks.map(chunk => 
    Promise.all(chunk.map(ws => buildWorkspace(ws)))
  )
  
  return Promise.all(promises)
}

2. 增量构建

javascript
async function buildIncremental(workspaces, changedFiles) {
  const affected = getAffectedWorkspaces(workspaces, changedFiles)
  
  for (const workspace of affected) {
    await buildWorkspace(workspace)
  }
}

3. 缓存构建

javascript
class BuildCache {
  constructor() {
    this.cache = new Map()
  }
  
  get(key) {
    return this.cache.get(key)
  }
  
  set(key, value) {
    this.cache.set(key, value)
  }
  
  has(key) {
    return this.cache.has(key)
  }
}

版本管理

语义化版本

javascript
function bumpVersion(version, type) {
  const [major, minor, patch] = version.split('.').map(Number)
  
  switch (type) {
    case 'major':
      return `${major + 1}.0.0`
    case 'minor':
      return `${major}.${minor + 1}.0`
    case 'patch':
      return `${major}.${minor}.${patch + 1}`
    default:
      return version
  }
}

版本同步

javascript
function syncVersions(workspaces, version) {
  for (const workspace of workspaces) {
    workspace.version = version
    updatePackageJson(workspace)
  }
}

总结

Monorepo 的架构体现了以下特点:

  1. 工作空间:使用工作空间隔离项目
  2. 依赖图:管理包之间的依赖关系
  3. 并行构建:充分利用多核 CPU
  4. 缓存优化:缓存构建结果

理解 Monorepo 的架构有助于更好地使用和优化 Monorepo。

参考资源

架构师AI杜公众号二维码

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态