限界上下文 = 系统的边界划分
解决什么问题:如何把一个复杂的业务领域,划分为多个独立演化的子系统—— 让每个子系统有自己的模型,自己的语言,自己的演化节奏。
业务问题
真实场景
场景 1:电商系统的"订单"歧义
订单服务团队说"订单" = {订单号, 商品列表, 价格, 支付状态}
物流团队说"订单" = {运单号, 收货地址, 派送状态}
财务团队说"订单" = {账单号, 金额, 收款状态}
→ 三个团队用同一个词,含义完全不同
→ 一个"订单"改动要协调三个团队场景 2:单体应用膨胀
业务跑通 1 年后:
- 单体代码库 50 万行
- 一个团队 20 人
- 部署频率 = 1 次/周(每次都有冲突)
- 一个 bug 影响所有业务场景 3:跨团队集成混乱
A 团队:Bounded Context 内部用"用户" = {ID, 邮箱, 状态}
B 团队:通过 HTTP API 调用 A,需要"用户" = {ID, 邮箱, 状态, 偏好, 历史订单}
→ A 团队每次内部重构都破坏 B
→ B 团队要跟着 A 改
→ 互相拖累失败的典型做法
做法 1:按技术层划分
✗ 把"订单服务"拆为:
- 订单 API 服务
- 订单业务逻辑服务
- 订单数据库服务
后果:业务完整性破坏,一个业务变更要协调三个服务做法 2:按团队规模均分
✗ 每个团队负责一个 BC,不管业务是否相关
后果:业务相关性被切断,跨 BC 调用爆炸做法 3:过早拆分
✗ 业务还没跑通就拆 5 个 BC
后果:复杂度 > 业务复杂度,协调成本 > 业务价值控制论视角
数学原型
钱学森《工程控制论》Ch 6 §6.3: "设法确定控制矩阵的元素 Ckν(s) 与 C′kμ(s) 须要满足什么条件, 才能使规定的控制信号 Xj(s) 与 Ξμ(s) 只影响和它们相应的受控量 Yj(s) 和 Wμ(s), 而不影响其余的受控量。"
钱学森 §6.3: "这里的数学问题也就是如何把图 6.2-4 中的系统矩阵加以对角线化的问题。"
控制论解法
多变量控制系统:
[ Y1 ] [ E11 E12 ... E1n ] [ W1 ]
[ Y2 ] = [ E21 E22 ... E2n ] [ W2 ]
[ .. ] [ ... ... ... ... ] [ .. ]
[ Yi ] [ Ei1 Ei2 ... Ein ] [ Wn ]
限界上下文 = 对角线化:
- 让每个 Wi 只影响 Yi
- 非对角元素 = 0(无跨上下文影响)
[ Y1 ] [ E11 0 ... 0 ] [ W1 ]
[ Y2 ] = [ 0 E22 ... 0 ] [ W2 ]
[ .. ] [ ... ... ... ... ] [ .. ]
[ Yi ] [ 0 0 ... Ein ] [ Wn ]与业务的对应
| 控制论概念 | 软件工程 |
|---|---|
| 互不影响子系统 | Bounded Context |
| 矩阵对角化 | BC 边界划分 |
| 控制矩阵元素 | 跨 BC 调用关系 |
| 受控量 | BC 内部状态 |
| 控制量 | BC 对外暴露的接口 |
核心概念
定义
限界上下文(Bounded Context):一个模型在其中成立的边界。同一个词在不同 BC 里可以有不同的含义;每个 BC 有自己的通用语言、自己独立演化的能力。
关键属性
| 属性 | 含义 | 软件工程对位 |
|---|---|---|
| 边界明确 | 模型只在 BC 内部成立 | 服务边界、模块边界 |
| 通用语言 | BC 内部用一致的术语 | Ubiquitous Language |
| 独立演化 | BC 改动不影响其他 BC | 服务独立部署 |
| 数据所有权 | BC 拥有自己的数据 | 数据库隔离 |
| 团队对齐 | 一个 BC 对应一个小团队 | 团队拓扑 |
边界划分的三层标准
┌────────────────────────────────────────┐
│ 业务完整性(最重要) │
│ - 这个 BC 能独立提供完整的业务价值吗? │
└────────────────────────────────────────┘
│
↓
┌────────────────────────────────────────┐
│ 团队规模(次要) │
│ - 一个 BC 对应一个 ≤ 10 人的小团队 │
└────────────────────────────────────────┘
│
↓
┌────────────────────────────────────────┐
│ 技术可行性(最后考虑) │
│ - 这个 BC 能否独立部署?独立扩缩? │
└────────────────────────────────────────┘工程实践
步骤 1:用 Event Storming 识别领域事件
输入:业务专家 + 技术人员一起工作坊
输出:
- 所有领域事件列表("订单已创建""支付已完成"等)
- 每个事件的发布者(聚合根)
- 每个事件的订阅者
工具:
- 便利贴(黄色 = 领域事件)
- 时间线(从左到右)步骤 2:识别聚合根与一致性边界
步骤:
1. 把领域事件按"必须同时发生"分组
2. 每个分组 = 一个聚合
3. 聚合的入口 = 聚合根
判断标准:
- 同一聚合内的对象,必须作为一个整体修改
- 不同聚合的对象,可以独立修改
例:
- 订单 + 订单项 = 一个聚合(订单是聚合根)
- 订单 + 用户 = 不同聚合(通过用户ID引用,不修改用户)步骤 3:识别 BC 边界
三种 BC 边界识别法:
1. **业务不变性识别法**
- 找出"不会随时间变化的核心业务规则"
- 每组不变性 = 一个 BC
2. **业务能力识别法**
- 业务能力(如"下订单""发货""收款")= BC
- 每个业务能力独立演化
3. **组织结构识别法**
- 团队边界 = BC 边界(康威定律)
- 适合组织已经定型的场景步骤 4:验证拆分粒度
┌──────────────┬────────────────────────────┐
│ 评估维度 │ 健康标准 │
├──────────────┼────────────────────────────┤
│ 内聚度 │ BC 内方法调用密度 > 80% │
│ 耦合度 │ 跨 BC 调用次数 / BC 内 < 0.2 │
│ 协调成本 │ 协调成本 < 业务总成本 20% │
│ 变更独立性 │ 一年内跨 BC 协调 < 20% │
│ 故障隔离 │ 一 BC 故障不影响其他 BC │
└──────────────┴────────────────────────────┘步骤 5:建立 Context Map
Context Map(上下文映射图):
[订单 BC] ──Customer-Supplier──> [支付 BC]
│ │
└──Shared Kernel──[用户模型]──┘
│
[物流 BC]──ACL──┘
每个 BC 之间的关系都要明确标注:
- Customer-Supplier(上下游)
- Shared Kernel(共享内核)
- Anti-Corruption Layer(防腐层)
- Conformist(跟随)
- Separate Ways(独立)常见反模式
反模式 1:技术驱动拆分
症状:按技术层(前端/后端/数据库)拆分
后果:业务完整性破坏
正解:业务驱动拆分反模式 2:拆分过细
症状:5 个服务做一个小业务
后果:协调成本 > 业务价值
正解:等到痛点出现再拆分反模式 3:拆分过粗
症状:单体 100 万行,所有业务挤在一起
后果:部署冲突、性能干扰、升级困难
正解:识别不变性,按业务能力拆分反模式 4:忽略组织对齐
症状:BC 边界与团队边界不一致
后果:康威定律反噬,跨团队协调爆炸
正解:BC 边界 ⊆ 团队边界工程实例:电商系统的 BC 划分
[用户 BC]
├─ 模型:用户 {ID, 邮箱, 偏好, 状态}
├─ 接口:注册、登录、修改偏好
└─ 团队:用户团队
[商品 BC]
├─ 模型:商品 {SKU, 名称, 价格, 库存}
├─ 接口:查询商品、调整库存
└─ 团队:商品团队
[订单 BC] ← 用户通过 ACL 调用
├─ 模型:订单 {订单号, 商品列表, 金额, 状态}
├─ 接口:创建订单、取消订单
└─ 团队:订单团队
[支付 BC] ← Customer-Supplier
├─ 模型:支付 {支付ID, 金额, 状态, 流水}
├─ 接口:发起支付、查询支付状态
└─ 团队:支付团队
[物流 BC] ← Customer-Supplier
├─ 模型:运单 {运单号, 收货地址, 状态}
├─ 接口:创建运单、更新状态
└─ 团队:物流团队性能指标
拆分质量指标
| 指标 | 设计目标 | 实际值 | 衡量方法 |
|---|---|---|---|
| 内聚度 | BC 内调用 > 80% | 待测 | Jaeger 调用链分析 |
| 耦合度 | 跨 BC 调用 / BC 内 < 0.2 | 待测 | 服务网格统计 |
| 协调成本 | 协调成本 < 业务总成本 20% | 待测 | 工时统计 |
| 变更独立性 | 一年内跨 BC 协调 < 20% | 待测 | PR / Git 数据分析 |
| 故障隔离 | 一 BC 故障不影响其他 BC | 待测 | 混沌工程 |
业务健康指标
| 指标 | 设计目标 | 实际值 | 衡量方法 |
|---|---|---|---|
| 新功能交付周期 | < 2 周 | 待测 | 研发效能 |
| 故障爆炸半径 | ≤ 1 BC | 待测 | 故障演练 |
| 部署频率 | 每 BC 独立可部署 | 待测 | CI/CD 数据 |
| 跨 BC 协调 PR 数 | < 20% | 待测 | GitHub 数据 |
监控设计
关键监控:
- BC 内调用次数(业务正常 = 高)
- 跨 BC 调用次数(业务耦合 = 低,但应监控)
- 跨 BC 调用延迟(业务集成健康)
- BC 故障率(隔离是否生效)
- 协调成本工时(团队健康)边界判断
何时不用
场景 1:单进程应用
- 业务量小(< 10 万用户)
- 团队规模小(< 5 人)
- 没有分布式假设
→ 不需要 BC,单体足够场景 2:业务完全跑通前
- 业务模式还没稳定
- 频繁改动的早期阶段
→ 不要过早 BC 划分,先跑通业务场景 3:业务量小但分布广
- 100 个 BC,每个 BC 用户数 < 100
→ 协调成本 > 业务价值
→ 应该合并 BC与其他方法论的边界
| 相邻概念 | 区别 |
|---|---|
| 微服务 | 微服务是技术实现;BC 是业务边界(一个 BC 可以是 1 个或多个微服务) |
| 模块 | 模块是代码组织;BC 是业务自治单元 |
| 子域 | 子域是问题空间的划分;BC 是解决方案空间的划分 |
| 聚合 | 聚合是数据一致性边界;BC 是业务自治边界 |
何时升级(更复杂的方法论)
当以下情况出现时,考虑更复杂的协调机制:
1. 跨 BC 协调 > 50% → 重新划分 BC
2. 一个 BC 超过 20 人 → 拆分 BC
3. BC 间一致性要求强 → 引入 Saga / 分布式事务
4. 业务规模暴涨 → 考虑 CQRS / Event Sourcing关键洞察
限界上下文不是技术决策,而是业务决策。
划分错误 = 业务协作成本爆炸。
划分的核心是"业务不变性"——识别哪些业务规则是稳定的,按不变性划分 BC。
相关链接
IDDD 对位
- BC = 战略边界 → 子域分类(Ch 2):一个子域至少一个 BC
- BC 间关系 → 9 种 BC 关系(Ch 3 ★★★★★):Partnership / Shared Kernel / Customer-Supplier / Published Language 等
- Module vs BC → Module vs Bounded Context(Ch 9):Module 是开发单元,BC 是业务边界
- 集成 → 集成成熟度模型(Ch 13):ACL 保护 BC 完整性
核心:BC 是 DDD 的"战略武器"——控制论 Ch 21"分散系统协调"在业务语言的形式化。