事件风暴模式:一场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. 混沌阶段(1-2小时):所有人往墙上贴事件,不用管顺序,想到什么贴什么。这个阶段的目标是「把脑子里的东西倒出来」。
  2. 时间线阶段(1小时):把事件按时间顺序排列,从左到右。你会发现很多事件其实是重复的,或者顺序不对。
  3. 命令和聚合阶段(2小时):在每个事件前面贴上触发它的命令,在事件后面贴上对应的聚合。这时候业务逻辑开始清晰了。
  4. 边界划分阶段(1小时):用大框框把相关的事件、命令、聚合圈起来,这就是限界上下文。

我做过最成功的一次工作坊,是一个保险理赔系统。团队花了三天时间,贴了200多张便利贴,最后理出了12个限界上下文。那个项目后来用了两年,架构基本没动过。

事件风暴与代码的映射

事件风暴不是纸上谈兵。它可以直接映射到代码结构:

  • 每个限界上下文对应一个微服务或模块
  • 每个聚合对应一个领域模型类
  • 每个命令对应一个方法或命令对象
  • 每个事件对应一个事件类

比如上面那个订单聚合,它的代码结构就是直接从事件风暴里推导出来的。

SVG:事件风暴核心流程

下面这张图,展示了一个事件风暴工作坊的核心流程。我建议你保存下来,下次做工作坊时贴在墙上。

事件风暴工作坊核心流程 混沌阶段 贴出所有领域事件 橙色便利贴 时间线阶段 按时间顺序排列 从左到右 命令聚合阶段 识别命令和聚合 蓝色+黄色便利贴 边界划分 工作坊输出 领域事件列表 (橙色) 命令列表 (蓝色) 聚合定义 (黄色) 限界上下文 (大框框) 每个阶段约1-2小时,总时长约6-8小时 建议参与人数:5-10人(领域专家+开发团队)

总结

事件风暴不是什么高深的理论,它就是一个让团队对齐认知的工具。我见过太多项目,需求文档写了一大堆,结果开发做出来的东西和业务想要的完全不一样。

如果你正在做一个复杂的业务系统,我建议你试试事件风暴。找一面大墙,买几包便利贴,把业务专家和开发拉到一个房间里。你会发现,很多争论在贴便利贴的过程中就解决了。

嗯,最后说一句:便利贴一定要买质量好的。我那次用便宜货,贴到一半全掉下来了,那场面...你懂的。

核心要点回顾:

  • 领域事件是过去时,命令是现在时
  • 聚合封装业务规则,保证数据一致性
  • 限界上下文划清业务边界,每个上下文独立演化
  • 事件风暴的输出可以直接映射到代码结构
公众号:蓝海资料掘金营,微信deep3321