4. 限界上下文(Bounded Context):定义、识别原则与上下文映射模式

限界上下文是领域驱动设计中最重要的战术模式之一,它划定了领域模型的边界,确保模型内部的术语、规则和逻辑保持一致性。没有限界上下文,领域模型将陷入“大泥球”式的混乱。本章将深入定义限界上下文,给出识别原则,并系统讲解上下文映射模式。

4.1 限界上下文的定义

限界上下文是一个显式的边界,领域模型在这个边界内具有特定的含义。它解决了“同一个词在不同场景下含义不同”的问题。例如,“客户”在销售上下文、售后上下文和财务上下文中,其属性、行为和规则完全不同。

上下文 “客户”的含义 关键属性 核心行为
销售上下文 潜在购买者 联系方式、意向产品、线索来源 跟进、报价、转化
订单上下文 下单者 收货地址、支付方式、历史订单 下单、支付、取消
售后上下文 服务请求者 保修状态、服务记录、满意度 报修、退换、评价
财务上下文 账务主体 信用额度、应收/应付、发票 对账、开票、催收
核心原则:一个限界上下文内部使用统一的通用语言(Ubiquitous Language),所有团队成员(领域专家、开发人员、产品经理)对同一个术语的理解完全一致。跨上下文时,术语的含义可以不同,但必须通过映射关系显式转换。

4.2 识别限界上下文的六大原则

识别限界上下文是领域建模的关键步骤。以下六大原则可以帮助架构师和领域专家准确划定边界:

  1. 业务能力原则:每个限界上下文对应一个独立的业务能力域。例如,“库存管理”、“订单处理”、“支付结算”各自构成一个上下文。
  2. 变化频率原则:如果两个业务概念的变化频率和原因不同,它们应该属于不同的上下文。例如,商品基本信息(名称、描述)变化少,而商品价格(促销、折扣)变化频繁,应分开。
  3. 团队组织原则:遵循康威定律,一个限界上下文通常由一个团队负责。如果两个团队维护同一个模型,容易产生冲突和耦合。
  4. 数据所有权原则:每个限界上下文拥有自己的数据存储,不直接访问其他上下文的数据库。数据通过接口或事件进行交换。
  5. 语义一致性原则:同一个术语在上下文内部必须具有唯一且明确的含义。如果“用户”在某个场景下既指“注册用户”又指“匿名访客”,说明边界不清晰。
  6. 部署独立性原则:限界上下文应该能够独立部署和扩展。如果两个模块必须同时部署才能工作,它们很可能属于同一个上下文。
实践技巧:在识别初期,可以使用“事件风暴(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 上下文映射关系图

下图展示了一个电商系统中典型的上下文映射关系,使用不同颜色区分上下文,箭头表示依赖方向:

电商系统上下文映射图 订单上下文 Order Context 支付上下文 Payment Context 库存上下文 Inventory Context 用户上下文 User Context 物流上下文 Logistics Context 营销上下文 Marketing Context 客户-供应商 防腐层 开放主机服务 发布语言 遵奉者 图例说明 • 箭头方向表示依赖方向:A → B 表示 A 依赖 B 提供的服务或数据。 • 客户-供应商:订单上下文作为客户,调用支付上下文的接口完成支付。 • 防腐层:订单上下文通过适配层隔离库存上下文的模型变更,避免污染。 • 遵奉者:营销上下文直接使用订单上下文的模型,不做额外转换。

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 来绘制和管理上下文映射图,支持从代码生成架构图。