DDD之用事件风暴重构“电商订单履约”(11)

事件风暴在DDD中的作用大家多多少少都有一定的了解,比如可以在业务、研发、测试等各方建立一套统一语言、能梳理一套全景的业务流程图、能帮助研发人员划定限界上下文、并且还可以指导代码的落地等等,本文从一个实际案例来介绍下DDD中事件风暴的运行过程,通过案例让大家对这种明星级的建模方法有一个更强的体感。

准备工作:颜色就是语言

在开始之前,必须约定便利贴颜色的含义(这是一套标准协议):

  • 🟧 橙色 - 领域事件 (Domain Event) :系统中已经发生的事实(核心!必须用过去式)。

  • 🟦 蓝色 - 命令 (Command):触发事件的动作/请求。

  • 🟨 黄色 - 角色 (Actor):发出命令的人或角色。

  • 🟪 紫色 - 策略/规则 (Policy):当事件 A 发生后,触发命令 B 的业务规则("当...就...")。

  • 🍠 粉色 - 外部系统 (External System):第三方的支付网关、物流公司等。

  • 🟩 绿色 - 读模型 (Read Model):用户做决定时需要看的信息(如商品详情页)。

  • 📒 浅黄 - 聚合 (Aggregate):承载业务逻辑的实体对象(DDD 的核心)。


第一步:发散风暴 ------ 寻找"领域事件"(橙色便利贴)

目标:不加过滤地把所有能想到的业务结果都贴出来。

场景:大家围着白板,业务方开始喊,开发开始贴。

  • 业务方:"用户下单成功了。" -> 🟧 订单已创建 (OrderCreated)

  • 业务方:"然后我们要扣库存。" -> 🟧 库存已锁定 (StockLocked)

  • 业务方:"支付成功后。" -> 🟧 支付已确认 (PaymentConfirmed)

  • 业务方:"仓库发货了。" -> 🟧 货物已发货 (GoodsShipped)

  • 业务方:"用户收到货签收。" -> 🟧 订单已签收 (OrderDelivered)

  • 业务方:"超时没支付,订单要取消。" -> 🟧 订单已取消 (OrderCancelled)

专家技巧

  1. 必须用过去式(Created, Paid, Shipped)。

  2. 按照大致的时间顺序,从左到右排列。


第二步:倒序推演 ------ 寻找"命令"与"角色"(蓝色 & 黄色)

目标 :为每一个事件寻找源头。问自己:"是谁,做了什么,导致了这个事件?"

我们从 "订单已创建" 这个事件倒推:

  1. 事件:🟧 订单已创建

  2. 命令:是谁触发的?是用户点击了"提交订单"按钮。

    • 贴上 🟦 提交订单 (Place Order)
  3. 角色:是谁点的?

    • 贴上 🟨 普通用户 (User)
  4. 读模型:用户点之前看了什么?

    • 贴上 🟩 购物车预览 (Cart View)

我们再看 "货物已发货" 这个事件:

  1. 事件:🟧 货物已发货

  2. 命令 :🟦 执行发货 (Ship Goods)

  3. 角色 :🟨 仓库管理员 (Warehouse Keeper)


第三步:识别"策略"与"外部系统" ------ 复杂度的藏身处(紫色 & 粉色)

目标:识别自动化逻辑和外部依赖。这是业务逻辑最复杂的"胶水"部分。

场景 1:自动化流转

  • 现象:用户支付成功后,不需要人去点按钮,仓库系统应该自动生成发货单。

  • 逻辑 支付已确认 时,就 生成发货单。

  • 操作

    1. 🟧 支付已确认

    2. 🟪 策略:当支付成功,且库存充足,触发发货 (Policy)

    3. 🟦 生成发货单 (Command)

场景 2:外部依赖

  • 现象:支付是在支付宝/微信里完成的。

  • 操作

    1. 🟦 请求支付 (Command)

    2. 🍠 第三方支付网关 (External System)

    3. 🟧 支付已确认 (Event)


第四步:寻找"聚合" ------ 划定领地(浅黄便利贴)

目标 :这是 DDD 落地最关键的一步。我们要把相关的命令和事件归类,找到负责维护状态的那个"东西"

我们需要问:"是由哪个对象来执行'提交订单'命令,并产生'订单已创建'事件的?"

  1. 聚合 1:订单 (Order)

    • 命令:提交订单、取消订单、修改地址

    • 事件:订单已创建、订单已取消、地址已修改

    • -> 用一张大的📒浅黄纸写上 Order,把上述蓝、橙条包起来。

  2. 聚合 2:库存 (Inventory)

    • 命令:锁定库存、释放库存

    • 事件:库存已锁定、库存已释放

    • -> 聚合根:Inventory

  3. 聚合 3:支付 (Payment)

    • 命令:支付、退款

    • 事件:支付已确认、退款已完成

    • -> 聚合根:Payment


第五步:划定"限界上下文" ------ 战略拆分

目标:在墙上画圈,决定微服务拆分的边界。

看着墙上的聚合分布,我们发现自然形成了几个聚类:

  1. 订单上下文 (Order Context):包含 Order 聚合。负责交易流程。

  2. 库存上下文 (Inventory Context):包含 Inventory 聚合。负责管货。

  3. 支付上下文 (Payment Context):包含 Payment 聚合。负责管钱。

上下文映射

  • 订单上下文 发出 订单已创建 事件。

  • 库存上下文 监听 这个事件,然后执行 锁定库存 命令。

  • 这就是微服务之间的事件驱动架构 (EDA) 原型。


第六步:从墙面到代码 ------ 落地映射

现在,墙上的便利贴可以直接映射为 Java 代码了。这就是 DDD 的威力。

|-------|----------|------------------------------------------------------|
| 便利贴颜色 | 业务概念 | 代码映射 (Java 示例) |
| 🟧 橙色 | 领域事件 | public class OrderCreatedEvent { String orderId; } |
| 🟦 蓝色 | 命令 | public void placeOrder(Command cmd) { ... } (应用服务方法) |
| 📒 浅黄 | 聚合根 | public class Order { ... } (实体类) |
| 🟪 紫色 | 策略 | OrderPolicyService 或 领域事件监听器 (@EventListener) |
| 🟩 绿色 | 读模型 | CQRS 中的 Query 端 DTO (如 OrderDetailDTO) |

代码落地演示

假设墙上有一组:

  • 🟦 命令:ChangeShippingAddress (修改收货地址)

  • 📒 聚合:Order

  • 🟧 事件:ShippingAddressChanged

对应的代码:

codeJava

复制代码
// 1. 命令对象 (对应蓝色)
public class ChangeAddressCommand {
    private String orderId;
    private Address newAddress;
    // getter/setter
}

// 2. 聚合根 (对应浅黄)
public class Order {
    private OrderId id;
    private Address address;
    private OrderStatus status;

    // 对应蓝色命令的行为方法
    public void changeAddress(Address newAddress) {
        // 业务规则校验
        if (this.status == OrderStatus.SHIPPED) {
            throw new DomainException("已发货订单不能修改地址");
        }
        
        this.address = newAddress;
        
        // 3. 发布事件 (对应橙色)
        DomainEventPublisher.publish(new ShippingAddressChangedEvent(this.id, newAddress));
    }
}

// 4. 应用服务 (连接层)
@Service
public class OrderAppService {
    @Resource private OrderRepository repo;

    public void handleChangeAddress(ChangeAddressCommand cmd) {
        Order order = repo.findById(cmd.getOrderId());
        order.changeAddress(cmd.getNewAddress()); // 调用聚合行为
        repo.save(order);
    }
}

总结

事件风暴的价值在于

它把隐晦的、藏在业务专家脑子里的逻辑,显性化地变成了墙上的"便利贴流程图"。

  • 橙色定义了结果。
  • 蓝色定义了API。
  • 浅黄定义了核心模型。
  • 画圈定义了微服务边界。

做完这一场风暴,你的系统架构图、核心类图、API 列表基本上就自动生成了 80%。这就是领域驱动设计最性感的地方。

欢迎关注,一起交流、一起进步~

相关推荐
我命由我123451 小时前
VSCode - VSCode 颜色值快速转换
前端·ide·vscode·前端框架·编辑器·html·js
AnAnCode1 小时前
ECS 架构 (Entity Component System) - 数据导向编程快速入门
java·架构·游戏服务器
qq_12498707531 小时前
基于SpringBoot+vue的小黄蜂外卖平台(源码+论文+部署+安装)
java·开发语言·vue.js·spring boot·后端·mysql·毕业设计
小二·1 小时前
Spring框架入门:TX 声明式事务详解
java·数据库·spring
前端涂涂1 小时前
怎么设计一个加密货币 谁有权利发行数字货币 怎么防止double spending attack 怎么验证交易合法性 铸币交易..
前端
JuneTT1 小时前
【JS】使用内连配置强制引入图片为base64
前端·javascript
i02081 小时前
Java 17 + Spring Boot 3.2.5 使用 Redis 实现“生产者–消费者”任务队列
java·spring boot·redis
烤麻辣烫1 小时前
黑马程序员苍穹外卖后端概览
xml·java·数据库·spring·intellij-idea
前端涂涂1 小时前
4.BTC-协议
前端