DDD 事件风暴实战:3小时工作坊产出电商订单领域模型
1. 为什么你需要这场高强度事件风暴工作坊?
想象一下这样的场景:你的团队正在开发一个电商系统,产品经理、开发工程师和业务专家围坐在会议室里,每个人对"订单状态流转"的理解都不尽相同。开发团队认为取消订单应该直接删除记录,业务方坚持需要保留取消原因;前端工程师设计的订单列表字段,和后端返回的数据结构总是对不上——这种认知偏差每天都在消耗团队宝贵的开发时间。
传统需求文档和会议讨论的局限性在于:
- 信息衰减:业务知识在传递过程中丢失关键细节
- 视角单一:各角色只关注自己负责的部分
- 反馈延迟:问题往往在开发后期才暴露
事件风暴工作坊通过可视化协作和时间盒约束解决了这些问题。我们设计的3小时高强度工作坊包含:
gantt title 3小时事件风暴工作坊时间分配 dateFormat HH:mm section 准备阶段 场地布置 :00:00, 30m 规则讲解 :00:30, 15m section 核心流程 识别领域事件 :00:45, 45m 构建命令模型 :01:30, 30m 划定聚合边界 :02:00, 30m section 收尾阶段 模型可视化 :02:30, 20m 复盘与改进 :02:50, 10m关键提示:工作坊成功的前提是确保所有核心角色全程参与,包括至少1名能拍板的业务专家
2. 工作坊前的精准准备清单
2.1 物料准备(电商订单场景示例)
| 物料类型 | 规格要求 | 用途说明 | 电商订单示例 |
|---|---|---|---|
| 橙色便利贴 | 7.5cm×7.5cm正方形 | 记录领域事件 | "订单已支付"、"库存已扣减" |
| 蓝色便利贴 | 7.5cm×7.5cm正方形 | 记录决策命令 | "提交订单"、"取消订单" |
| 黄色便利贴 | 7.5cm×12.5cm长方形 | 标识聚合根 | "订单聚合"、"支付聚合" |
| 紫色便利贴 | 5cm×7.5cm长方形 | 标记热点问题 | "跨境税费计算规则不明确" |
| 绿色便利贴 | 7.5cm×7.5cm正方形 | 表示读模型 | "订单详情视图" |
| 白板墙 | 至少4米宽 | 贴便利贴的平面 | 按时间线排列事件 |
| 计时器 | 手机或专用计时器 | 控制每个环节时间 | 每环节严格计时 |
2.2 人员配置黄金比例
# 工作坊参与者计算器(适用于中型电商项目) def calculate_participants(team_size): domain_experts = max(2, team_size // 5) # 20%业务专家 tech_leads = max(1, team_size // 6) # 技术决策者 developers = team_size - domain_experts - tech_leads return { "业务专家(产品/运营)": domain_experts, "技术负责人(架构师)": tech_leads, "开发测试人员": developers } # 示例:12人团队配置 print(calculate_participants(12))输出结果:
{ "业务专家(产品/运营)": 3, "技术负责人(架构师)": 2, "开发测试人员": 7 }2.3 场地布置关键细节
- 拆除会议桌椅:迫使参与者保持站立状态,提升专注度
- 分区规划:
- 左侧:业务流程图区域(用户旅程)
- 中部:核心建模区(事件风暴)
- 右侧:问题停车场(收集争议点)
- 设备检查:
- 确保空调温度适宜(建议22-24℃)
- 准备高像素手机/相机记录白板内容
- 测试无线投屏设备(如需远程参与)
3. 工作坊核心流程拆解
3.1 领域事件识别(45分钟)
操作步骤:
- 主持人讲解规则:"用过去时态描述业务事件,如'订单已创建'"
- 采用"1-2-All"技术:
- 1分钟个人思考
- 2分钟小组讨论(3人一组)
- 全员贴出事件卡片
- 事件归类:
- 垂直轴表示时间顺序
- 水平轴表示业务模块(订单、支付、物流等)
电商订单典型事件序列:
1. 购物车已生成 2. 订单已创建 3. 支付已发起 4. 库存已预留 5. 支付已成功 6. 订单已确认 7. 物流单已生成 8. 商品已出库 9. 订单已送达 10. 订单已完成常见陷阱:避免将技术事件(如"数据库已更新")与业务事件混淆
3.2 命令与聚合识别(30分钟)
命令-事件映射表:
| 命令(蓝色) | 触发事件(橙色) | 聚合(黄色) |
|---|---|---|
| 提交购物车 | 购物车已生成 | 购物车聚合 |
| 创建订单 | 订单已创建 | 订单聚合 |
| 发起支付 | 支付已发起 | 支付聚合 |
| 扣减库存 | 库存已预留 | 库存聚合 |
| 确认收货 | 订单已完成 | 订单聚合 |
聚合设计原则检查清单:
- [ ] 每个聚合是否有明确的根实体?
- [ ] 聚合间的引用是否通过ID而非对象?
- [ ] 聚合边界是否与业务一致性边界匹配?
- [ ] 单个聚合能否在1秒内完成加载?
3.3 处理争议热点(实战技巧)
当团队对"订单取消是否应该立即释放库存"产生分歧时:
- 5分钟计时辩论:正反方各陈述理由
- 业务专家裁决:明确业务规则
- 记录决策依据:在紫色便利贴上注明:
决策:取消订单时异步释放库存 原因:避免高并发下库存超卖 责任人:@张伟(架构师)
4. 可视化成果输出
4.1 电商订单领域模型图例
[用户] --(提交订单)--> [订单聚合] --(订单已创建)--> [支付聚合] | v (订单已取消) --[库存聚合] <--(释放库存)4.2 上下文映射关系
# 使用PlantUML生成上下文映射图(示例) @startuml package "订单上下文" { [订单服务] --> [支付服务] [订单服务] --> [库存服务] } package "支付上下文" { [支付服务] --> [第三方支付网关] } package "物流上下文" { [物流服务] --> [仓储系统] } @enduml4.3 工作坊产出物清单
- 领域模型图(Visio/Lucidchart格式)
- 事件流文档(Markdown格式)
- 待解决问题列表(Excel表格)
- 术语表(包含中英文对照)
5. 避坑指南:我们踩过的三个坑
坑1:业务专家中途离场
- 现象:讨论支付流程时财务负责人提前离开
- 解决方案:现在我们会:
- 提前锁定关键人物时间
- 准备业务专家替补人选
- 录制关键决策过程
坑2:陷入技术实现细节
- 现象:争论是否使用MQ实现事件总线
- 应对策略:
def handle_technical_discussion(): if discussion.level > ModelDesign: print("[主持人] 这个问题记入停车场,会后由技术组专项讨论") move_to_parking_lot() else: continue_workshop()
坑3:时间失控
- 错误示范:某个环节超时导致模型不完整
- 我们的改进:
- 配备专职时间管理员
- 使用沙漏增加时间压迫感
- 对超时环节设置"加时投票"
6. 工作坊后的跟进动作
- 48小时内发送会议纪要给所有参与者确认
- 第3天召开1小时的技术设计会,将模型转化为:
- 服务划分方案
- API接口定义
- 数据模型设计
- 第5天生成领域模型与代码的映射文档,例如:
// 订单聚合根示例 public class Order { private OrderId id; private List<OrderItem> items; private OrderStatus status; public void cancel(String reason) { this.status = OrderStatus.CANCELLED; registerEvent(new OrderCancelled(id, reason)); } }这场高强度事件风暴的魔力在于:当业务专家看着满墙的便利贴突然说"原来我们的订单流程这么复杂",而开发工程师同时惊呼"我终于明白为什么要这些字段"时——你知道团队已经建立了真正的统一语言。这不是终点,而是持续领域精炼的开始。