4. 限界上下文(Bounded Context):定义、识别原则与上下文映射模式
限界上下文是领域驱动设计中最重要的战术模式之一,它划定了领域模型的边界,确保模型内部的术语、规则和逻辑保持一致性。没有限界上下文,领域模型将陷入“大泥球”式的混乱。本章将深入定义限界上下文,给出识别原则,并系统讲解上下文映射模式。
4.1 限界上下文的定义
限界上下文是一个显式的边界,领域模型在这个边界内具有特定的含义。它解决了“同一个词在不同场景下含义不同”的问题。例如,“客户”在销售上下文、售后上下文和财务上下文中,其属性、行为和规则完全不同。
| 上下文 | “客户”的含义 | 关键属性 | 核心行为 |
|---|---|---|---|
| 销售上下文 | 潜在购买者 | 联系方式、意向产品、线索来源 | 跟进、报价、转化 |
| 订单上下文 | 下单者 | 收货地址、支付方式、历史订单 | 下单、支付、取消 |
| 售后上下文 | 服务请求者 | 保修状态、服务记录、满意度 | 报修、退换、评价 |
| 财务上下文 | 账务主体 | 信用额度、应收/应付、发票 | 对账、开票、催收 |
核心原则:一个限界上下文内部使用统一的通用语言(Ubiquitous Language),所有团队成员(领域专家、开发人员、产品经理)对同一个术语的理解完全一致。跨上下文时,术语的含义可以不同,但必须通过映射关系显式转换。
4.2 识别限界上下文的六大原则
识别限界上下文是领域建模的关键步骤。以下六大原则可以帮助架构师和领域专家准确划定边界:
- 业务能力原则:每个限界上下文对应一个独立的业务能力域。例如,“库存管理”、“订单处理”、“支付结算”各自构成一个上下文。
- 变化频率原则:如果两个业务概念的变化频率和原因不同,它们应该属于不同的上下文。例如,商品基本信息(名称、描述)变化少,而商品价格(促销、折扣)变化频繁,应分开。
- 团队组织原则:遵循康威定律,一个限界上下文通常由一个团队负责。如果两个团队维护同一个模型,容易产生冲突和耦合。
- 数据所有权原则:每个限界上下文拥有自己的数据存储,不直接访问其他上下文的数据库。数据通过接口或事件进行交换。
- 语义一致性原则:同一个术语在上下文内部必须具有唯一且明确的含义。如果“用户”在某个场景下既指“注册用户”又指“匿名访客”,说明边界不清晰。
- 部署独立性原则:限界上下文应该能够独立部署和扩展。如果两个模块必须同时部署才能工作,它们很可能属于同一个上下文。
实践技巧:在识别初期,可以使用“事件风暴(Event Storming)”工作坊,让领域专家和开发团队一起在墙上贴出领域事件、命令和聚合,自然形成上下文边界。通常,一个聚合组就是一个候选的限界上下文。
4.3 上下文映射模式
限界上下文之间需要协作,上下文映射模式描述了这些协作关系。Eric Evans 在《领域驱动设计》中定义了多种模式,以下是实践中最重要的七种:
| 模式名称 | 关系类型 | 适用场景 | 耦合度 |
|---|---|---|---|
| 合作关系(Partnership) | 对等 | 两个团队紧密协作,共同演进 | 高 |
| 共享内核(Shared Kernel) | 对等 | 共享一小部分核心模型,但保持边界 | 中 |
| 客户-供应商(Customer-Supplier) | 单向 | 上游(供应商)提供接口,下游(客户)消费 | 中 |
| 遵奉者(Conformist) | 单向 | 下游完全遵循上游模型,不做转换 | 高 |
| 防腐层(Anti-Corruption Layer) | 单向 | 下游通过适配层隔离上游模型的变更 | 低 |
| 开放主机服务(Open Host Service) | 单向 | 上游提供标准化的API供多个下游调用 | 低 |
| 发布语言(Published Language) | 单向 | 使用标准的数据格式(如XML、JSON Schema)交换 | 低 |
常见误区:不要试图在所有上下文之间使用“共享内核”。共享内核虽然方便,但会导致模型耦合,一旦变更需要协调多个团队。优先选择“防腐层”或“开放主机服务”来降低耦合。
4.4 上下文映射关系图
下图展示了一个电商系统中典型的上下文映射关系,使用不同颜色区分上下文,箭头表示依赖方向:
4.5 防腐层(Anti-Corruption Layer)深度解析
防腐层是实践中最重要的上下文映射模式之一。当两个上下文需要集成,但下游不希望被上游的模型污染时,在下游侧建立一个转换层,将上游模型翻译为下游自己的模型。
防腐层的典型结构:
// 上游上下文(外部系统)的模型
public class ExternalOrder {
public String orderId;
public String customerName;
public List<ExternalItem> items;
}
// 下游上下文(我们的系统)的模型
public class Order {
public String id;
public String customer;
public List<OrderLine> lines;
}
// 防腐层:适配器
public class OrderAdapter {
public Order translate(ExternalOrder external) {
Order order = new Order();
order.id = external.orderId;
order.customer = external.customerName;
order.lines = external.items.stream()
.map(item -> new OrderLine(item.productId, item.quantity))
.collect(Collectors.toList());
return order;
}
}
防腐层的优势:
- 保护下游模型的完整性,不受上游模型变更的影响。
- 当上游系统替换或升级时,只需修改防腐层,下游核心逻辑不变。
- 允许下游使用自己的通用语言,保持语义一致性。
4.6 上下文映射的实践建议
在实际项目中,上下文映射不是一次性的工作,而是持续演进的。以下是一些实践建议:
- 从大粒度开始:先识别粗粒度的上下文(如订单、支付、库存),再逐步细化。
- 优先使用低耦合模式:默认选择“开放主机服务”或“防腐层”,避免过早使用“共享内核”。
- 记录映射关系:使用上下文映射图(如上面的SVG图)记录所有上下文之间的依赖关系,作为架构文档的一部分。
- 定期评审:随着业务发展,上下文边界可能发生变化,定期组织事件风暴工作坊重新审视边界。
- 避免循环依赖:上下文之间的依赖应该是有向无环图(DAG),避免A依赖B、B又依赖A的情况。
工具推荐:可以使用 Context Mapper 工具(基于 Eclipse)或 Structurizr 来绘制和管理上下文映射图,支持从代码生成架构图。