DDD、事件源和聚合状态的形状

DDD, Event Sourcing, and the shape of the Aggregate state

我很难理解应用该实体的事件与该实体的数据投影得出的 state 的形状。

Aggregate 的状态是否仅用于确定命令是否可以成功应用?或者该状态应该以其他方式使用吗?

一个例子 - 我有一个标准博客 Post 实体 post。我可能会遇到 postCreatedpostPublishedpostUnpublished 等事件。对于我将坚持阅读表格的预测,我需要一个基础 posts(这将包括所有 posts,无论状态如何,有很多细节)以及 published_posts 投影(它只代表当前发布的 posts,只有信息渲染所必需的。

在上述情况下,我的聚合状态是否应该仅用于确定 post 是否可以发布或取消发布等?如果是这种情况,我的状态在聚合中的形状是否纯粹由这些验证所需的内容定义? 例如,在我的基础 post 投影中,我想要一个包含对 post 进行更改的所有用户的列表。在 aggregate/commands 的验证方面,我一点也不关心已进行更改的用户列表。这是否意味着此列表不应该 成为我所在州的一部分?

TL;DR:是 - 将聚合中的“状态”限制为您选择缓存以支持数据更改的数据。


在我的聚合中,我区分了两种不同的想法:

  • 历史,也就是描述聚合生命周期变化的事件序列
  • 缓存,也就是我们隐藏的数据值,因为每次查询事件历史都很糟糕。

我们永远不会使用的缓存结果没有太多价值。

CQRS 的一个基本教训是我们不需要 聚合 无处不在

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. -- Evans, 2003

如果我们不更改数据,那么我们可以安全地直接使用数据的不可变副本。

聚合的唯一基本目的是确定需要应用哪些事件(如果有)以使聚合的状态与命令一致(如果可以使聚合如此一致)。所有不需要的状态都可以卸载到读取端,这可以被认为是事件流的混合(每个读取端只维护它需要的状态)。

也就是说,在实践中,直接使用聚合状态是有原因的,主要的原因是希望聚合具有更强的一致性:CQRS 本质上是最终一致的。对于一致性更新的所有问题,重要的是要认识到一致性不是免费的,而且通常甚至不便宜;我倾向于认为一个项目具有一致性预算,但我对它的支出非常吝啬。

在您的情况下,可能没有理由在聚合状态中包含更改 post 的用户列表,除非例如有类似“没有单个用户可以修改给定的 post 超过 n 次”。