事件风暴模式:一场DDD建模的头脑风暴
说实话,我第一次接触事件风暴(Event Storming)时,心里是有点抵触的。你想想看,一群人围着一面墙,贴满花花绿绿的便利贴,这能解决什么架构问题?
直到我在一个电商项目中,团队花了三个月画UML图,结果需求一变,图全废了。那次之后,我才真正理解事件风暴的价值——它不是画图,而是让领域专家和开发团队用同一种语言对话。
什么是事件风暴?
事件风暴是一种工作坊形式的建模方法。说白了,就是一群人聚在一起,通过识别「领域事件」来理解业务。
它的核心思想很简单:业务是由一系列事件驱动的。比如「订单已创建」「支付已成功」「商品已发货」——这些都是领域事件。
核心要素:
- 领域事件(Domain Event):已经发生的事情,用橙色便利贴
- 命令(Command):触发事件的动作,用蓝色便利贴
- 聚合(Aggregate):业务逻辑的载体,用黄色便利贴
- 限界上下文(Bounded Context):业务边界的划分,用大框框
领域事件:一切建模的起点
我个人习惯,每次做事件风暴,第一件事就是让团队列出所有「已经发生的事」。
比如一个订单系统:
- 订单已创建
- 订单已支付
- 库存已扣减
- 订单已发货
- 订单已取消
这些事件用过去时态命名,因为它们是事实。我在项目中遇到过,有人把「创建订单」写成事件——这是不对的。「创建订单」是命令,事件应该是「订单已创建」。
小技巧:事件命名用「名词+动词过去式」,比如「OrderCreated」「PaymentReceived」。这样一眼就能看出是已经发生的事实。
命令:谁触发了事件?
有了事件,接下来要问:这个事件是怎么发生的?
比如「订单已创建」这个事件,是由「提交订单」这个命令触发的。命令通常来自用户、外部系统或者定时器。
我曾经在一个支付项目中,漏掉了「支付超时」这个命令,结果导致大量订单卡在待支付状态。嗯,从那以后我特别强调:命令不只是用户操作,还包括系统内部的触发条件。
| 领域事件 | 触发命令 | 执行者 |
|---|---|---|
| 订单已创建 | 提交订单 | 用户 |
| 支付已成功 | 确认支付 | 支付网关 |
| 库存已扣减 | 扣减库存 | 系统内部 |
| 订单已超时 | 超时检查 | 定时任务 |
聚合:业务逻辑的守护者
聚合是DDD里最容易被误解的概念。我见过有人把聚合当成数据库表,也有人把聚合当成微服务。
其实聚合就是一组业务逻辑的集合,它保证数据的一致性。比如「订单」聚合,它包含订单项、收货地址、支付信息——这些数据必须一起变化。
在事件风暴中,聚合用黄色便利贴表示。它接收命令,产生事件。比如:
// 订单聚合
public class Order {
private OrderId id;
private List<OrderItem> items;
private OrderStatus status;
// 命令:提交订单
public OrderCreated submit() {
// 业务校验
if (items.isEmpty()) {
throw new IllegalArgumentException("订单不能为空");
}
// 产生事件
return new OrderCreated(this.id);
}
// 命令:确认支付
public PaymentReceived confirmPayment() {
this.status = OrderStatus.PAID;
return new PaymentReceived(this.id);
}
}
注意看,聚合内部封装了业务规则。你不能直接修改订单状态,只能通过命令来操作。这就是聚合的核心价值——保证业务规则不被绕过。
限界上下文:划清业务边界
限界上下文是事件风暴里最大的概念。它代表一个独立的业务领域,有自己的语言和规则。
比如一个电商系统,可能有这些限界上下文:
- 订单上下文:处理订单创建、支付、发货
- 库存上下文:管理商品库存
- 支付上下文:对接支付网关
- 物流上下文:处理配送
每个上下文内部,术语的含义可能不同。比如「订单」在订单上下文里是「待支付的订单」,在物流上下文里是「待配送的包裹」。
避坑指南:我曾经在一个项目中,把「用户」这个概念放在所有上下文里共享。结果订单上下文改了用户字段,支付上下文也跟着崩了。后来我学乖了:每个上下文的数据模型应该是独立的,通过事件来同步。
事件风暴工作坊:实战流程
好了,理论说完了。咱们来点实际的。一个典型的事件风暴工作坊,我一般分四步走:
- 混沌阶段(1-2小时):所有人往墙上贴事件,不用管顺序,想到什么贴什么。这个阶段的目标是「把脑子里的东西倒出来」。
- 时间线阶段(1小时):把事件按时间顺序排列,从左到右。你会发现很多事件其实是重复的,或者顺序不对。
- 命令和聚合阶段(2小时):在每个事件前面贴上触发它的命令,在事件后面贴上对应的聚合。这时候业务逻辑开始清晰了。
- 边界划分阶段(1小时):用大框框把相关的事件、命令、聚合圈起来,这就是限界上下文。
我做过最成功的一次工作坊,是一个保险理赔系统。团队花了三天时间,贴了200多张便利贴,最后理出了12个限界上下文。那个项目后来用了两年,架构基本没动过。
事件风暴与代码的映射
事件风暴不是纸上谈兵。它可以直接映射到代码结构:
- 每个限界上下文对应一个微服务或模块
- 每个聚合对应一个领域模型类
- 每个命令对应一个方法或命令对象
- 每个事件对应一个事件类
比如上面那个订单聚合,它的代码结构就是直接从事件风暴里推导出来的。
SVG:事件风暴核心流程
下面这张图,展示了一个事件风暴工作坊的核心流程。我建议你保存下来,下次做工作坊时贴在墙上。
总结
事件风暴不是什么高深的理论,它就是一个让团队对齐认知的工具。我见过太多项目,需求文档写了一大堆,结果开发做出来的东西和业务想要的完全不一样。
如果你正在做一个复杂的业务系统,我建议你试试事件风暴。找一面大墙,买几包便利贴,把业务专家和开发拉到一个房间里。你会发现,很多争论在贴便利贴的过程中就解决了。
嗯,最后说一句:便利贴一定要买质量好的。我那次用便宜货,贴到一半全掉下来了,那场面...你懂的。
核心要点回顾:
- 领域事件是过去时,命令是现在时
- 聚合封装业务规则,保证数据一致性
- 限界上下文划清业务边界,每个上下文独立演化
- 事件风暴的输出可以直接映射到代码结构