事件存储根聚合与聚合
event store root aggregate vs aggregate
事件存储中的 "root aggregate" 和 "aggregate" 有什么区别?
即使经过数小时的搜索,我仍无法准确定义这些内容。我的理解是,聚合是一个 table 的 ID,它通常将商店内的事件(集合)与对象表示组合在一起,这就是为什么也可以有聚合类型的原因。
此外,我看到聚合 table 带有版本号,我发现它们在本质上是 group/container/aggregate 表示事件集合的 ID 的前提下令人困惑。
DDD 书籍和术语并未专门针对事件溯源(它还不是一件事)。
SE Radio podcast 226 with Eric Evans 稍微介绍一下。
聚合 是一组相关的实体和值对象,在聚合中,为单个事物(或相关的一组事物)建模事情)需要保持内部一致
聚合根可以是查看聚合的根实体'through'(例如,您拥有它的发票实体和一组单独的订单行)
不幸的是(但命名是困难的)人们觉得有必要重载术语聚合来指代在流中执行事件聚合的事物(即使相关事件集一起确实代表聚合的状态)。
在 Implementing DDD 一书中,功能事件溯源附录很好地将它分开了 IIRC - 我倾向于将这个东西(为了制作目的而聚合的相关事件的聚合)称为 'the folded state'(并将这种折叠状态的演变与实际决策分开)
Projections(用 DDD-CQRS-ES 的说法)指的是一组抽象的事物,这些事物悬挂在事件之外,这些事物对新事件做出适当的反应(代表人们做出的决定)制成)。投影可以维护 非规范化视图 ala 你的 'summary' (不只有一个)。流具有事件。 decision-making 过程将折叠这些事件以推断相关上下文以做出决策。 projection 可以有状态(根据 well-ordered 观察与流(或其集合)相关的事件的相关子集) - 它可能是一个 blob保存在 key-value 商店中,随时可以轻松查询。您每次从头开始阅读构建的每个流的总数可能是 in-memory。它可能是一个类似的 in-memory 缓存,它由某种形式的检查点(一个人看到的最后一个事件编号)以及一个序列化形式的检查点支持。它可以是其中的许多。或者在退化的情况下,您可能还没有连接任何东西。
所以我们同意您正在尝试将事件流制作成 DDD 聚合根和聚合模式。
有一种常见的模式,即每个聚合都有自己的流。它具有聚合根的名称和身份,尽管名称部分不是必需的。
整个聚合上的所有操作都由事件表示,然后写入此流。
因此,如果您有一个聚合根 MyNamespace.Order
,它有多个子值对象或实体 MyNamespace.OrderLine
,所有行上的操作都是通过访问聚合根方法完成的,并且所有事件都写入一个流,因此对于 ID 为 123 的订单,它将是:
直播:我的名字
- space.Order-123
- 已下订单
- OrderDeliveryAddressSet
- 已添加订单行
- 已添加订单行
- OrderLineRemoved
- 订单确认
- OrderETASet
- 订单已付
- 已发货
- 已交付订单
实体的概念未在事件存储端表示,因为您无法轻松定义不同流之间的关系以从多个流读取事件以重构聚合。因此,所有事件都在聚合根流中写入和读取,然后成为整个聚合的 流。
关于聚合的一个重要规则是聚合也是一致性边界。这基本上意味着聚合上的所有操作都必须在一个事务中完成。
保持事件版本处理并发很重要。当您从事件存储中读取聚合时,您也会获得最新的事件版本。执行操作后,您将新事件写回,检查最新的事件版本是否仍然相同。如果最后一个事件版本不匹配,您将抛出并发异常,因为有人已经更改了聚合并将更改写入存储,并且您遇到了冲突。
关于"tables"我不太清楚你的意思。当然,您可以在表中对事件存储进行建模,并且有不同的策略可以做到这一点。通常情况下,您不会真的更喜欢使用专门的事件存储,例如 this one.
事件存储中的 "root aggregate" 和 "aggregate" 有什么区别?
即使经过数小时的搜索,我仍无法准确定义这些内容。我的理解是,聚合是一个 table 的 ID,它通常将商店内的事件(集合)与对象表示组合在一起,这就是为什么也可以有聚合类型的原因。
此外,我看到聚合 table 带有版本号,我发现它们在本质上是 group/container/aggregate 表示事件集合的 ID 的前提下令人困惑。
DDD 书籍和术语并未专门针对事件溯源(它还不是一件事)。
SE Radio podcast 226 with Eric Evans 稍微介绍一下。
聚合 是一组相关的实体和值对象,在聚合中,为单个事物(或相关的一组事物)建模事情)需要保持内部一致
聚合根可以是查看聚合的根实体'through'(例如,您拥有它的发票实体和一组单独的订单行)
不幸的是(但命名是困难的)人们觉得有必要重载术语聚合来指代在流中执行事件聚合的事物(即使相关事件集一起确实代表聚合的状态)。
在 Implementing DDD 一书中,功能事件溯源附录很好地将它分开了 IIRC - 我倾向于将这个东西(为了制作目的而聚合的相关事件的聚合)称为 'the folded state'(并将这种折叠状态的演变与实际决策分开)
Projections(用 DDD-CQRS-ES 的说法)指的是一组抽象的事物,这些事物悬挂在事件之外,这些事物对新事件做出适当的反应(代表人们做出的决定)制成)。投影可以维护 非规范化视图 ala 你的 'summary' (不只有一个)。流具有事件。 decision-making 过程将折叠这些事件以推断相关上下文以做出决策。 projection 可以有状态(根据 well-ordered 观察与流(或其集合)相关的事件的相关子集) - 它可能是一个 blob保存在 key-value 商店中,随时可以轻松查询。您每次从头开始阅读构建的每个流的总数可能是 in-memory。它可能是一个类似的 in-memory 缓存,它由某种形式的检查点(一个人看到的最后一个事件编号)以及一个序列化形式的检查点支持。它可以是其中的许多。或者在退化的情况下,您可能还没有连接任何东西。
所以我们同意您正在尝试将事件流制作成 DDD 聚合根和聚合模式。
有一种常见的模式,即每个聚合都有自己的流。它具有聚合根的名称和身份,尽管名称部分不是必需的。
整个聚合上的所有操作都由事件表示,然后写入此流。
因此,如果您有一个聚合根 MyNamespace.Order
,它有多个子值对象或实体 MyNamespace.OrderLine
,所有行上的操作都是通过访问聚合根方法完成的,并且所有事件都写入一个流,因此对于 ID 为 123 的订单,它将是:
直播:我的名字
- space.Order-123
- 已下订单
- OrderDeliveryAddressSet
- 已添加订单行
- 已添加订单行
- OrderLineRemoved
- 订单确认
- OrderETASet
- 订单已付
- 已发货
- 已交付订单
实体的概念未在事件存储端表示,因为您无法轻松定义不同流之间的关系以从多个流读取事件以重构聚合。因此,所有事件都在聚合根流中写入和读取,然后成为整个聚合的 流。
关于聚合的一个重要规则是聚合也是一致性边界。这基本上意味着聚合上的所有操作都必须在一个事务中完成。
保持事件版本处理并发很重要。当您从事件存储中读取聚合时,您也会获得最新的事件版本。执行操作后,您将新事件写回,检查最新的事件版本是否仍然相同。如果最后一个事件版本不匹配,您将抛出并发异常,因为有人已经更改了聚合并将更改写入存储,并且您遇到了冲突。
关于"tables"我不太清楚你的意思。当然,您可以在表中对事件存储进行建模,并且有不同的策略可以做到这一点。通常情况下,您不会真的更喜欢使用专门的事件存储,例如 this one.