4. 从事件风暴到领域模型:识别领域事件、命令与聚合根、生成统一语言与限界上下文映射
事件风暴(Event Storming)是一种协作建模技术,它通过高密度的“工作坊”形式,让领域专家与开发团队在同一个画布上快速发现业务全貌。本章将讲解如何把事件风暴的输出(领域事件、命令、聚合)转化为可落地的领域模型,并最终生成统一语言(Ubiquitous Language)与限界上下文(Bounded Context)映射图。
4.1 事件风暴的核心产出物
一次完整的事件风暴工作坊通常会产生以下三类核心元素,它们构成了领域驱动设计(DDD)的原始素材:
| 元素类型 | 颜色(标准) | 描述 | 示例 |
|---|---|---|---|
| 领域事件 | 橙色便签 | 业务中已经发生的、不可改变的事实,通常使用“过去时”命名 | 订单已提交、库存已扣减、支付已完成 |
| 命令 | 蓝色便签 | 触发领域事件的用户意图或系统指令,通常使用“动词+宾语”命名 | 提交订单、扣减库存、确认支付 |
| 聚合根 | 黄色便签(大) | 一组具有事务一致性的实体和值对象的边界,是命令的执行者 | 订单聚合、库存聚合、支付聚合 |
4.2 从领域事件到命令:识别触发关系
每个领域事件都对应一个明确的触发者——命令。命令由角色(Actor)或系统定时器发起,经过聚合根的校验与处理后,产生一个或多个领域事件。以下是一个典型的电商下单流程的识别示例:
- 用户发起
提交订单命令 - 订单聚合校验商品库存、用户余额后,产生
订单已提交事件 - 库存聚合监听
订单已提交事件,执行扣减库存命令,产生库存已扣减事件 - 支付聚合监听
库存已扣减事件,执行发起支付命令,产生支付已发起事件
4.3 聚合根的设计:事务一致性的边界
聚合根是领域模型中最重要的概念之一,它定义了事务一致性的边界。在事件风暴画布上,聚合根通常表现为一个包含多个实体和值对象的“黄色大框”。设计聚合根时,需要遵循以下规则:
- 强一致性边界: 聚合内部的所有状态变更必须在同一个事务中完成。
- 外部引用仅通过ID: 聚合根之间不能直接持有对象引用,只能通过全局唯一标识符(ID)关联。
- 一个聚合一个根: 每个聚合有且只有一个根实体,外部只能通过根实体访问聚合内部。
- 小聚合原则: 聚合不宜过大,通常一个聚合包含3~5个实体/值对象为佳。
4.4 统一语言(Ubiquitous Language)的生成
统一语言是DDD的基石,它确保领域专家、开发人员、测试人员、产品经理使用同一套术语进行沟通。在事件风暴画布上,每张便签的名称都应该成为统一语言的一部分。生成统一语言的步骤如下:
| 步骤 | 活动 | 产出物 |
|---|---|---|
| 1 | 收集画布上所有便签的名称 | 原始术语列表 |
| 2 | 消除同义词和歧义词(如“用户”与“客户”统一为“客户”) | 标准化术语表 |
| 3 | 为每个术语编写简短的定义(不超过两句话) | 术语定义表 |
| 4 | 将术语表纳入项目Wiki或代码仓库的Glossary文件中 | 活文档 |
以下是一个电商领域的统一语言示例片段:
# 电商领域统一语言(Glossary)
## 订单(Order)
- 定义:客户在一次购物中提交的商品购买请求,包含一个或多个订单行。
- 状态:待支付、已支付、已发货、已完成、已取消。
## 订单行(OrderLine)
- 定义:订单中的单个商品条目,包含商品ID、数量、单价。
- 约束:一个订单行只能属于一个订单。
## 库存(Inventory)
- 定义:商品的可销售数量,以SKU为单位。
- 操作:扣减、回滚、冻结。
4.5 限界上下文映射(Context Map)
限界上下文是DDD中划分系统边界的核心工具。在事件风暴画布上,我们可以根据“聚合根”的归属和“领域事件”的订阅关系,识别出不同的限界上下文。常见的上下文关系模式包括:
- 合作关系(Partnership): 两个上下文互相依赖,需要同步协调。
- 共享内核(Shared Kernel): 两个上下文共享一部分模型(如共享值对象)。
- 客户-供应商(Customer-Supplier): 上游上下文为下游上下文提供数据或服务。
- 防腐层(Anti-Corruption Layer): 下游上下文通过适配器隔离上游上下文的模型变化。
- 开放主机服务(Open Host Service): 上游上下文通过API对外暴露服务。
- 分离方式(Separate Ways): 两个上下文完全独立,不进行任何集成。
4.6 从事件风暴到代码模型的映射
当事件风暴画布上的元素被识别并归类到限界上下文后,我们可以将其直接映射为Go工程中的代码结构。以下是一个典型的映射规则:
| 事件风暴元素 | Go代码元素 | 示例 |
|---|---|---|
| 领域事件 | 结构体 + 事件接口 | type OrderSubmitted struct { OrderID string } |
| 命令 | 结构体 + 命令接口 | type SubmitOrder struct { UserID string; Items []OrderItem } |
| 聚合根 | 结构体 + 领域方法 | type Order struct { ID string; Items []OrderLine; Status OrderStatus } |
| 限界上下文 | Go package(模块) | package order、package inventory |
| 统一语言 | 类型别名、常量、注释 | type OrderStatus string + 常量定义 |
internal/ 子目录,例如 internal/order/、internal/inventory/、internal/payment/。每个子目录内部再按 domain/(领域模型)、application/(应用服务)、infrastructure/(基础设施)分层组织代码。
4.7 本章小结
从事件风暴到领域模型的转化,本质上是将“业务事实”(领域事件)逆向推导为“业务意图”(命令),再通过“业务边界”(聚合根)进行封装,最终形成“业务语言”(统一语言)和“系统边界”(限界上下文)。这个过程不仅产出了高质量的领域模型,更重要的是建立了团队对业务的一致理解。在下一阶段,我们将基于这些模型进行分布式事务的架构选型。