4. 限界上下文详解
限界上下文(Bounded Context)是领域驱动设计的核心战术模式,它划定了模型、术语和业务规则的明确边界。在 OpenAPI 规范中,每个限界上下文通常对应一组独立的 API 端点集合、Schema 定义和业务逻辑。理解限界上下文及其协作模式,是设计高内聚、低耦合 API 的关键。
4.1 限界上下文的定义与特征
限界上下文是一个显式的边界,领域模型在这个边界内部保持一致性,而边界外部的模型可以完全不同。每个限界上下文拥有自己的:
- 通用语言(Ubiquitous Language):团队内部统一使用的术语和概念
- 业务规则:独立的业务逻辑和约束条件
- 数据模型:可能与其他上下文共享概念但结构不同
- API 契约:对外暴露的接口定义
关键原则:两个限界上下文中的同名概念(如“订单”)可能具有完全不同的属性和行为。不要试图创建全局统一的“企业级数据模型”,这会导致耦合和僵化。
| 特征 | 说明 | OpenAPI 映射 |
|---|---|---|
| 边界性 | 模型只在边界内有效 | 每个上下文对应独立的 API 规范文件或标签 |
| 一致性 | 内部模型无歧义 | Schema 定义在上下文内唯一且完整 |
| 自治性 | 可独立演进和部署 | API 版本、服务器地址独立管理 |
| 语言统一 | 团队使用相同术语 | operationId、参数名、Schema 名称保持一致 |
4.2 上下文映射图
上下文映射图(Context Map)描述了不同限界上下文之间的协作关系。它帮助团队理解系统整体的集成方式,识别潜在的冲突点。常见的映射关系包括:
- 合作关系(Partnership):两个上下文协同完成一个用例
- 共享内核(Shared Kernel):共享部分模型但保持独立
- 客户-供应商(Customer-Supplier):上游提供下游所需的数据
- 防腐层(Anti-Corruption Layer):隔离外部模型的侵入
- 开放主机服务(Open Host Service):通过 API 对外暴露能力
- 发布语言(Published Language):使用标准格式交换数据
- 分离方式(Separate Ways):完全独立,无集成
实践建议:在 OpenAPI 设计中,使用
tags 字段标记每个端点所属的限界上下文。例如,所有订单相关的端点使用 tags: [Orders],便于在 API 文档中按上下文分组查看。
4.3 共享内核
共享内核(Shared Kernel)是指两个或多个限界上下文共享一部分领域模型。这些共享部分通常是高度稳定、变更频率极低的核心概念。共享内核需要团队间紧密协作,任何变更都需要多方评审。
4.3.1 共享内核的特征
- 小规模:只共享真正必要的部分,避免“共享过多”导致耦合
- 高稳定性:共享模型变更需要跨团队审批
- 版本同步:所有使用共享内核的上下文必须同步升级
- 测试共享:共享内核的测试用例对所有上下文可见
4.3.2 OpenAPI 中的共享内核实现
在 OpenAPI 规范中,共享内核可以通过 $ref 引用公共的 Schema 文件来实现:
# shared-kernel.yaml — 共享内核定义
components:
schemas:
Money:
type: object
properties:
amount:
type: number
format: decimal
currency:
type: string
pattern: '^[A-Z]{3}$'
required: [amount, currency]
Address:
type: object
properties:
street:
type: string
city:
type: string
postalCode:
type: string
country:
type: string
pattern: '^[A-Z]{2}$'
required: [street, city, country]
# order-service.yaml — 订单上下文引用共享内核
paths:
/orders:
post:
tags: [Orders]
requestBody:
content:
application/json:
schema:
type: object
properties:
items:
type: array
items:
$ref: './shared-kernel.yaml#/components/schemas/Money'
shippingAddress:
$ref: './shared-kernel.yaml#/components/schemas/Address'
注意:共享内核的变更会传播到所有引用它的上下文。建议将共享内核的版本号独立管理,并在 OpenAPI 规范中使用
info.version 字段标注共享内核的版本。
4.4 防腐层
防腐层(Anti-Corruption Layer,ACL)是一种保护机制,防止外部系统的模型和概念“污染”当前限界上下文的纯净模型。当两个上下文使用不同的通用语言时,ACL 负责翻译和适配。
4.4.1 防腐层的职责
| 职责 | 说明 | OpenAPI 实现方式 |
|---|---|---|
| 模型翻译 | 将外部模型转换为内部模型 | 在 API 网关或 BFF 层进行 Schema 转换 |
| 协议适配 | 将外部协议转为内部协议 | 使用 API 组合模式或适配器模式 |
| 验证与过滤 | 过滤无效或恶意数据 | 在 OpenAPI 的 requestBody 中定义严格的 Schema 验证 |
| 缓存与降级 | 应对外部系统不可用 | 在 API 规范中定义备用响应和错误码 |
4.4.2 防腐层的 OpenAPI 设计模式
以下示例展示了一个防腐层如何将外部遗留系统的“客户”模型转换为当前上下文所需的“用户”模型:
# anti-corruption-layer.yaml — 防腐层 API
openapi: 3.0.3
info:
title: 客户信息防腐层
version: 1.0.0
description: 将遗留系统的 Customer 模型转换为当前上下文的 User 模型
paths:
/users/{externalCustomerId}:
get:
tags: [ACL]
summary: 通过外部客户ID获取内部用户信息
parameters:
- name: externalCustomerId
in: path
required: true
schema:
type: string
pattern: '^CUST-\d{6}$'
responses:
'200':
description: 成功转换的用户信息
content:
application/json:
schema:
$ref: '#/components/schemas/InternalUser'
'502':
description: 外部系统不可用
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
InternalUser:
type: object
properties:
userId:
type: string
description: 内部用户ID(由ACL生成)
displayName:
type: string
description: 从外部 Customer.name 转换而来
email:
type: string
format: email
roles:
type: array
items:
type: string
description: 从外部 Customer.type 映射的角色
required: [userId, displayName, email]
ErrorResponse:
type: object
properties:
code:
type: string
example: 'EXTERNAL_SERVICE_UNAVAILABLE'
message:
type: string
example: '无法连接到遗留客户系统,请稍后重试'
4.5 限界上下文与 OpenAPI 规范的最佳实践
- 按上下文拆分 API 规范文件:每个限界上下文拥有独立的 OpenAPI 文件,通过
$ref引用共享内核 - 使用标签分组:在
tags中明确标注每个端点所属的上下文 - 定义上下文边界文档:在
info.description中描述该上下文的职责和边界 - 防腐层独立部署:将 ACL 作为独立的 API 网关或 BFF 服务,拥有自己的 OpenAPI 规范
- 共享内核版本管理:使用语义化版本号,并在
info.version中标注
总结:限界上下文是 DDD 和 API 设计的基石。通过上下文映射图理清系统间的协作关系,利用共享内核减少重复定义,借助防腐层保护内部模型的纯净性。在 OpenAPI 规范中,这些模式可以通过文件拆分、标签分组和 Schema 引用等机制优雅地实现。