【问题标题】:Multi-Entity Aggregate Best Practices : when to create the first "sub" entity in an Axon Aggregate多实体聚合最佳实践:何时在轴突聚合中创建第一个“子”实体
【发布时间】:2022-01-10 09:00:58
【问题描述】:

(从 Axon 论坛重新定位此讨论 https://discuss.axoniq.io/t/multi-entity-aggregate-best-practices-when-to-create-the-first-entity/3827)

Axon 文档建议通过 EventSourcingHandler 将新实体添加到聚合中……

来自:Multi-Entity Aggregates - Axon Reference Guide

实体的创建发生在其父级的事件源处理程序中。 因此,实体类上不可能有一个“命令处理构造函数”,就像聚合根一样。

(原文:注意“it's”中的撇号是错字)

但是您何时/在何处创建初始实体?

您的 GiftCard 聚合开始时没有实体。我们有一个类似的模型,但希望在创建聚合时创建一个初始实体。例如想象一下,如果新的礼品卡总是带有“默认”礼品卡交易。 你会在哪里做呢?

a) 在礼品卡的构造函数中:

 public GiftCard(IssueCardCommand cmd) {
    ... 
    // problem: never gets replayed!
    transactions.add(new GiftCardTransaction(/*defaults*/)

b) 在初始化事件处理程序中

public void on(CardIssuedEvent evt) {    // initialize aggregate
      // create the first entity
      // gets replayed when the aggregate creation is replayed
     transactions.add(new GiftCardTransaction(/*defaults*/)
}

c) 在事件处理程序中但通过触发事件

public void on(CardIssuedEvent evt) {     // initialize aggregate
       // fire the event to create the first entity
      // problem: gets replayed twice: when the aggregate creation is replayed and the subsequent event is replayed!!?
      apply(new CardRedeemedEvent(/*defaults*/);
   }

d) 与 c 相同,但使用注解来防止重复播放:

public void on(CardIssuedEvent evt) {    // initialize aggregate
      // call a non-replayable method to fire the event
      createFirstEntity();
 }

@DisallowReplay  
public void createFirstEntity() {
      // The replay of this CardRedeemedEvent will create the first entity on replay, 
      // but the replay of the CardIssuedEvent will not, thanks to the @DisallowReplay... right??
      apply(new CardRedeemedEvent(/*defaults*/);
 }

【问题讨论】:

    标签: domain-driven-design axon


    【解决方案1】:

    使用事件源存储库时,正确答案是 b。您不应在事件源处理程序中应用事件。事件源处理程序应该只更新聚合的状态。每次处理新命令时,在命令处理开始之前,将针对该聚合的空实例重播具有该命令的 AggregateId 的所有事件。这就是为什么您不应该在 EventSourcing 处理程序中应用事件。实体的创建是第一个事件的副作用。 Disallow replay 可以用在使用 EventHandler 注解的方法或类中,而不是 EventSourcingHandlers。

    【讨论】:

    • 感谢 Yvonne,但我担心选项 b。依赖于命令中语句的顺序。即 b 中的命令处理程序方法。将触发 2 个事件:聚合创建事件,然后第一个实体创建事件。这依赖于以确切顺序处理这两个触发,以确保在添加实体之前初始化聚合。但是,选项 c 保证聚合已初始化。另外,我从文档中引用的内容是否保证即使我添加任何特殊的“禁止”注释也不会产生副作用?
    • @rhubarb Axon 确保订单被保留。因此,如果命令处理程序触发 2 个事件,这些事件将以相同的顺序重播。如果您查看事件存储,您可以找到 aggregateSequenceNumber。
    【解决方案2】:

    Vaishnavi 在 Axon 论坛上回答了这个问题:

    在您的示例选项 b 中,应使用 @EventSourcingHandler 创建实体。状态变化应该发生在这里,第一个事件处理程序应该设置聚合标识符。这是对聚合文档的参考。

    希望这对聚合创建实体有所帮助。

    但我想知道这是否不会混淆从@EventSourcingHandler 为创建实体的事件 的实体的实例化+添加(这确实看起来正确),我的情况是选项b正在为创建 Aggregate

    事件的处理程序中创建第一个实体

    实际上在仔细重新阅读文档后,我想知道是否 c.不是正确答案:

    来自:Command Handlers - Axon Reference Guide

    Axon 将忽略 apply() 在采购给定聚合时重放历史事件时调用

    这句话的意思是我在c中的评论。关于重播两次的“问题”是错误(Axon 足够聪明,知道事件处理程序中的apply 总是会导致双重重播,所以在重播时忽略它......?)

    c也是。正确答案?

    具体来说: 如果您想在多实体聚合中创建初始实体,则在处理 聚合 创建事件的 Event(Sourcing)Handler 中创建它

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 2015-09-28
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 2012-02-19
      相关资源
      最近更新 更多