战略设计:限界上下文的识别与映射
战略设计是 DDD 落地微服务架构的起点。它不关心某个实体如何持久化、某个值对象如何校验,而是回答一个更根本的问题:系统应该被切分成哪些自治的业务单元?这些单元之间如何协作?本章聚焦三个核心实践:限界上下文(Bounded Context)的识别、上下文映射图(Context Map)的绘制,以及核心域/支撑域/通用域的划分。
3.1 限界上下文:微服务的业务边界
限界上下文是 DDD 中最重要的战术概念。它定义了一个显式的业务边界,边界内部有统一的领域模型、通用语言和业务规则;边界外部则通过明确的接口进行交互。
| 维度 | 说明 | 微服务映射 |
|---|---|---|
| 模型一致性 | 同一个术语在上下文内只有唯一含义 | 一个微服务内部不应出现同名异义的数据结构 |
| 自治性 | 上下文可以独立演进、独立部署 | 微服务拥有自己的数据库、部署流水线 |
| 边界清晰 | 上下文之间通过事件或 API 通信 | 禁止跨服务直接访问数据库或共享表 |
| 通用语言 | 团队使用统一的业务术语沟通 | 代码中的包名、类名、事件名与业务术语一致 |
识别技巧:寻找“术语歧义点”。当同一个词在不同团队或模块中有不同含义时,通常就是限界上下文的边界。例如“客户”在销售上下文指潜在买家,在售后上下文指已签约用户,在财务上下文指付款方——这三个上下文应当分离。
3.2 上下文映射图:协作关系的可视化
上下文映射图(Context Map)用图形化的方式描述限界上下文之间的协作模式。它帮助架构师看清哪些上下文是上下游关系、哪些需要共享内核、哪些通过事件异步解耦。
3.2.1 六种协作模式
| 模式 | 方向 | 耦合度 | 适用场景 |
|---|---|---|---|
| 合作者 (Partnership) | 双向 | 高 | 两个上下文需要同步协调才能完成一个业务用例 |
| 共享内核 (Shared Kernel) | 双向 | 极高 | 共享一小部分核心模型,但需严格控制变更 |
| 客户-供应商 (Customer-Supplier) | 上游→下游 | 中 | 上游提供数据,下游按上游接口开发 |
| 遵奉者 (Conformist) | 上游→下游 | 中 | 下游完全复用上游模型,不做转换 |
| 防腐层 (Anti-Corruption Layer) | 下游→上游 | 低 | 下游需要隔离上游的模型变化,做翻译适配 |
| 开放主机服务 (Open Host Service) | 上游→下游 | 低 | 上游发布稳定的 API 协议,供多个下游消费 |
Go 实现提示:防腐层在 Go 中通常表现为一个独立的
adapter 包。例如,订单服务调用外部支付网关时,在 internal/adapter/payment 中定义接口和转换函数,将外部 DTO 转换为内部领域模型。这样即使支付网关接口变更,也只需修改 adapter 层。
3.3 核心域 / 支撑域 / 通用域
领域划分帮助团队聚焦资源。不是所有上下文都值得投入同等的设计精力和基础设施成本。
- 核心域 (Core Domain):系统最核心的竞争力,需要自研、深度优化、投入最优秀的工程师。
- 支撑域 (Supporting Subdomain):核心域的必要辅助,但无差异化价值,可适度简化或外包。
- 通用域 (Generic Subdomain):行业通用能力,优先采购或使用开源方案。
| 类型 | 业务价值 | 差异化 | 推荐策略 | Go 微服务示例 |
|---|---|---|---|---|
| 核心域 | 高 | 高 | 自研、持续重构、DDD 战术设计 | 订单引擎、定价规则、风控模型 |
| 支撑域 | 中 | 低 | 自研但简化设计,避免过度工程 | 用户通知、报表生成、后台审核 |
| 通用域 | 低 | 无 | 采购 SaaS 或集成开源组件 | 邮件发送、短信网关、对象存储 |
常见误区:团队容易把所有上下文都当作核心域来设计,导致每个微服务都引入事件溯源、CQRS、六边形架构等重型模式。正确的做法是:核心域用 DDD 战术设计,支撑域用 CRUD + 简单分层,通用域直接调 SDK。过度设计是微服务失败的首要原因之一。
3.4 实战:从业务事件流识别上下文
以下是一个经过验证的识别流程,适用于从零开始或重构遗留系统:
- 列举业务事件:与业务专家一起列出系统必须响应的事件(如“订单已提交”“库存不足”“支付超时”)。
- 找出事件归属:每个事件只能属于一个上下文。如果同一个事件出现在两个团队的业务语言中,说明边界不清晰。
- 识别聚合根:每个上下文内部至少有一个聚合根(Aggregate Root)作为入口。
- 绘制映射图:用上下文映射图标注上下文之间的协作模式。
- 标记领域类型:为每个上下文打上核心域/支撑域/通用域的标签。
3.5 在 Go 中表达限界上下文
Go 的包结构天然适合表达限界上下文。每个上下文对应一个顶级包,包内按职责分层:
// 订单上下文 —— 核心域
internal/
└── order/
├── domain/ # 领域模型:实体、值对象、聚合根
│ ├── order.go
│ ├── line_item.go
│ └── order_repository.go // 接口
├── application/ # 应用服务:编排用例
│ └── order_service.go
├── infrastructure/ # 基础设施:数据库、消息队列实现
│ ├── order_repository_impl.go
│ └── event_publisher.go
└── interfaces/ # 接口适配器:HTTP handler、gRPC、DTO
├── http/
└── grpc/
// 库存上下文 —— 支撑域
internal/
└── inventory/
├── domain/
├── application/
├── infrastructure/
└── interfaces/
包命名规范:Go 社区推荐使用小写单数形式作为包名(如
order 而非 orders)。每个限界上下文作为一个独立 Go module 时,使用 go.mod 中的 module path 区分,例如 github.com/company/order-service 和 github.com/company/inventory-service。
3.6 本章小结
- 限界上下文是微服务拆分的第一性原理,比技术拆分(按表拆分、按功能拆分)更稳定。
- 上下文映射图提供了六种协作模式,帮助团队显式约定服务间的通信方式。
- 核心域/支撑域/通用域的划分决定了资源投入优先级,避免在非核心能力上过度设计。
- Go 的包结构和 module 机制可以低成本地映射限界上下文,无需额外框架。