限界上下文策略

深入 DDD 限界上下文策略:限界上下文的识别与划分方法,上下文映射 Context Map 的绘制,共享内核、防腐层 ACL、开放主机服务等协作模式,以及团队边界与上下文边界的对齐实践。

微服务边界画在哪里,是架构师最头疼的问题之一。按"表"拆、按"模块"拆都试过,最后还是会互相纠缠。DDD 限界上下文给出了一条回答:以领域模型的完整性为界——每个上下文有自己的术语、模型与规则,上下文之间通过明确的关系协作。本文讲清限界上下文的识别、上下文映射,以及共享内核、防腐层、开放主机服务三种关键模式。

1. 什么是限界上下文

1.1 一个词,两个含义

同一个业务词汇在不同场景下含义不同。以"客户"为例:

上下文“客户"的含义
下单上下文有地址、能下单的买家
客服上下文有工单、有聊天记录的求助者
征信上下文有信用评分、有违约记录的实体

如果不加边界,把这些"客户"硬塞进一个模型,就会产生术语冲突与模型污染——A 上下文的规则被 B 上下文误用。

1.2 限界上下文的三要素

  • 模型:一组只在本上下文内有意义的领域对象;
  • 边界:模型不与外界共享,内部完整自洽;
  • 语言:上下文的"无处不在语言”(Ubiquitous Language),术语只在边界内有效。

一句话:限界上下文 = “一个模型 + 一道边界 + 一套术语”——它把大而全的统一模型,切成一簇小而自治的局部模型。

1.3 与微服务边界的关系

限界上下文是逻辑边界,微服务是物理边界。好的实践是一个上下文一个服务(或一个上下文一组服务),但也可以一个上下文先在模块化单体内实现,再渐进拆分(见模块化单体篇)。上下文错了,服务拆得再细也是乱拆。


2. 识别限界上下文的方法

2.1 从业务能力出发

先找高内聚的业务能力,每个能力通常对应一个上下文:

业务能力地图:
  订单管理 → 下单上下文
  库存管理 → 库存上下文
  支付结算 → 支付上下文
  客户服务 → 客服上下文
  商品运营 → 商品上下文

2.2 从语言变化出发

语言是识别边界最灵敏的信号。当同一个词在两个团队口中含义不同,这里就有一条上下文边界。开会时注意记录:“这个词你们是什么意思?”

2.3 从变化原因出发

“同一时间、同一原因发生的变化应放在同一上下文”——问自己:改客户地址字段,会不会连带触发客服工单结构的变化?如果会,说明边界画错了。

2.4 识别清单

信号边界线索
术语冲突同一名词多团队含义不同
变化耦合一处修改连锁触发多处
团队边界与组织结构强相关
数据归属同一数据被多处按不同规则改

2.5 事件风暴辅助识别

事件风暴(Event Storming)是快速识别上下文的实操方法:召集业务与开发,把领域事件(橙贴)、命令(蓝贴)、聚合(黄贴)铺满一面墙,事件密集扎堆的区域往往就是一个上下文。

事件风暴示意:
[订单已提交] [库存已扣减] → 订单上下文区
[库存已扣减] [补货单已生成] → 库存上下文区
两张"库存已扣减"贴纸分属两区 → 边界就在中间

一句话:识别限界上下文 = 顺着语言冲突、变化耦合与团队边界划界——术语变了,模型就该分家;事件风暴是把这种直觉变成集体演练的加速器。


3. 上下文映射 Context Map

3.1 什么是上下文映射

上下文映射把上下文之间的关系画成一张地图,让架构从"一堆服务"变成"一组有明确关系的域"。每种关系都有名字、方向与力度。

3.2 常见关系类型

关系方向说明
防腐层(ACL)单向下游隔离上游模型的差异
开放主机服务(OHS)单向上游发布公共接口,下游订阅
发布语言(PL)单向与 OHS 配套的公共数据格式
共享内核(SK)双向双方共享同一小模型
客户/供应商(C/S)单向下游是上游的客户
一致共生(CF)双向两个模型必须同步演进(要警惕)

3.3 示例地图

                  共享内核
             ┌─────────────────┐
             │  订单上下文      │
             │  (OHS/PL 发布)  │
             └───┬───────────┬─┘
                 │           │
         [ACL]   │           │   [ACL]
                 ▼           ▼
           ┌──────────┐  ┌──────────┐
           │ 库存上下文 │  │ 支付上下文 │
           └──────────┘  └──────────┘

一句话:上下文映射是限界上下文之间的关系契约——它把"两个服务怎么协作"显式化,比口头对接更可靠。


4. 共享内核 Shared Kernel

4.1 原理

两个上下文共同维护同一小块核心模型(如用户、合同号),用版本化共享包发布,双方都遵守一致规则。

共享内核(common 包)
  ├── User.java
  ├── OrderNo.java
  └── Money.java
  下游上下文 A ──依赖──┐
  下游上下文 B ──依赖──┘

4.2 适用与风险

优点风险
避免重复实现核心规则共享模型变动牵一发动全身
小范围共享代价低边界被共享打通,自治受损

4.3 使用原则

  • 共享范围尽量小,只放稳定、通用、规则不变的对象;
  • 共享内核要有版本管理与明确的变更流程(谁改、谁评审);
  • 一旦共享范围失控,就该拆成"发布语言 + 防腐层"。

一句话:共享内核适合少量高价值且稳定的共享模型——像夫妻共用一个小账户,前提是双方都守规矩、改动走评审。


5. 防腐层 Anti-Corruption Layer

5.1 为什么要防腐

下游上下文不希望被上游的模型、术语、数据结构污染——下游要自己的模型,而不是被迫使用上游的 DTO 与领域规则。

5.2 防腐层的结构

在下游入口建立一层翻译层,把上游的模型翻译成下游自己的模型:

上游服务(订单上下文)
        │  REST/gRPC
        ▼
ACL 防腐层(在下游侧)
  - 上游 DTO → 下游领域对象
  - 适配协议差异、重命名术语
  - 隔离上游变更
        ▼
下游服务(库存上下文,只认识自己的模型)

5.3 代码示例

// 下游库存上下文:防腐层把上游订单翻译成本地模型
public class OrderTranslator {
    public LocalOrder toLocal(RemoteOrder remote) {
        return new LocalOrder(
            remote.getOrderNo(),              // 字段映射
            remote.getTotalAmount().toLocal(), // 金额类型适配
            LocalStatus.of(remote.getStatus()) // 术语翻译
        );
    }
}

5.4 防腐层边界职责

  • 协议适配:上游 v1/v2、不同接口风格;
  • 术语翻译:把上游语言翻译为下游语言;
  • 变更缓冲:上游改了,只改 ACL,不改下游核心。

一句话:防腐层是下游自保的护城河——上游怎么变都行,只要翻译层更新,下游的领域模型保持纯净。


6. 开放主机服务 Open Host Service

6.1 原理

上游上下文把一部分能力以公开、稳定、通用的接口(OHS)暴露给下游,并配套**发布语言(Published Language)**作为双方共用的数据格式。

上游(商品上下文)
  OHS: /api/products  (稳定契约)
  PL:  ProductDto (公共数据格式)
         │
         ▼
下游1 下游2 下游3(都订阅同一契约,互不感知)

6.2 与 RPC 服务的区别

维度内部 RPCOHS
受众单一下游多个下游/外部
契约随调用方定制公共稳定
语义操作导向领域能力导向
版本常变演进受控

6.3 实践要点

  • OHS 用公共语义定义,不用任何一方的私有模型;
  • PL 是独立 schema,不与内部模型绑定;
  • OHS 变更走版本化发布,下游按语义化版本升级。

一句话:开放主机服务是上游"面向订阅者"发布公共能力——契约稳定、语义公共,多个下游各自消费,互不污染。


7. 上下文协作模式的选择

7.1 决策表

场景推荐模式
共享少量稳定核心模型共享内核
下游不想被上游污染防腐层
上游面向多下游发布能力OHS + 发布语言
上下游强耦合且必须同步客户/供应商(慎用一致共生)
模型不兼容又必须协作防腐层 + OHS 组合

7.2 团队与上下文对齐

一个限界上下文最好由一个团队长期负责。上下文是代码边界,也是组织边界——Conway 定律决定了组织结构终将映射到架构结构。上下文与团队错位,是技术债的温床。

7.3 演进路径

上下文映射不是一次画完就不动:随业务变化,共享内核会收紧成 OHS,一致共生会被拆成防腐层。地图要持续维护,如同架构决策记录一样留档。

7.4 ACL 与 OHS 的组合示例

真实系统常组合使用:上游用 OHS 发布,下游用 ACL 承接翻译,两边模型各保持纯净。

上游订单上下文                   下游库存上下文
  领域模型 ──→ OHS /orders ──→ ACL 翻译层 ──→ 本地模型
              (PL 契约)         (DTO→Local)      (StockReservation)
// 下游 ACL 调用上游 OHS
public void reserveFromOrder(String orderId) {
    OrderDto dto = orderClient.getOrder(orderId); // 调 OHS 契约
    LocalOrder local = orderTranslator.toLocal(dto); // ACL 翻译
    stockDomain.reserve(local.getLineItems());       // 纯领域逻辑
}

一句话:模式选择看关系——稳定共享用共享内核,防污染用防腐层,广播能力用开放主机服务;并让一个上下文归属一个团队。上游 OHS 发布、下游 ACL 承接的组合,是跨上下文协作的默认姿势。


8. 踩坑清单

坑现象对策
全公司统一模型一个"用户"到处串按上下文拆分模型
共享内核失控改动波及所有下游收紧共享范围 + 评审
防腐层过薄上游字段漏进下游强制翻译层 + 模型映射
OHS 与内部模型耦合改内部导致接口变PL 独立 schema
一致共生泛滥两服务必须一起改拆成 ACL 或合并
上下文地图过期关系失真、无人维护定期评审 + 留档
团队与上下文错位一个上下文多人乱改对齐组织边界
术语无人统一会议里鸡同鸭讲维护术语表

9. 总结

维度结论
边界依据语言、变化、团队、数据
关系表达上下文映射 Context Map
防污染防腐层 ACL
广播能力开放主机服务 + 发布语言
稳定共享共享内核(小范围)
组织对齐一上下文一团队

一句话记住:限界上下文划出"模型的国界",上下文映射画出"国与国的外交关系"——共享内核是同盟、防腐层是海关、开放主机服务是外交使馆,边界清晰,微服务才不会内战。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「架构」更多文章

  1. 绞杀者模式(Strangler Fig)
  2. 功能开关与发布工程
  3. 边车模式(Sidecar)