通过桌面游戏和战场理解 DDD Aggregates
Source: Dev.to
一个聚合是桌面游戏
把桌面游戏想象成一个聚合。一个游戏就是一个聚合。它有角色、道具和事件——但最重要的是,它有规则。在游戏内部的每一个动作都必须遵守这些规则。卡坦岛的规则只在卡坦岛生效。你不能把大富翁的“经过“起点”收取 200 美元”规则带进去。
卡坦岛本身就是聚合——不是角色,也不是道具,而是整个游戏本身。当你把这些角色和道具放进聚合内部后,就可以设计命令来操作它们。因此,聚合保护其不变式实际上就像桌面游戏执行其规则。唯一的区别是,在真实世界里,这些规则被称为业务规则。而且在真实世界里,规则是无法逃避的。
在同一回合内,所有动作必须一次性完成——不能只做一半动作再回来继续。这对应了另一个核心概念:
**聚合是事务一致性边界。**要么全部成功,要么全部失败。不存在中间状态。
战场 vs. 作战指挥室
真实的战场是一个聚合。指挥官的沙盘是另一个聚合。在沙盘上,旗帜代表每位将军——但旗帜本身并不是将军。它只是一个标识符(ID),告诉指挥官“此人存在”。将军的完整状态保存在战场上。
这两个聚合对同一个人的关注点完全不同。沙盘不需要知道将军受伤的程度或剩余的部队数量。它只需要知道:“他是否仍然可用?”
这就是为什么在聚合边界之间只能存储 ID——绝不能存储对象引用。边界必须保持清晰。
当战场上发生事件时,信使会骑马前往作战指挥室,指挥官们更新沙盘。这个信使就是 DDD 所称的策略(Policy)。它监听由一个聚合触发的领域事件(Domain Event),然后决定是否在另一个聚合中触发响应。
这正是 DDD 与事件溯源(EventSourcing)和 CQRS 天然契合的原因——整个系统通过在边界之间流动的事件协作,而不是直接调用或共享对象。事件。
总结
当这两个概念恍然大悟后,许多以前卡住的设计决策瞬间变得清晰。
我不确定这些类比是否适用于所有人,但对我而言,它们是我第一次感受到 DDD 不再是一堆术语。有时卡住并不是因为你不够努力——而是因为你还没有找到合适的切入角度。
如果你也有类似的顿悟时刻,欢迎在评论中分享你的经历。