多实体聚合最佳实践:何时在 Axon 聚合中创建第一个 "sub" 实体

Multi-Entity Aggregate Best Practices : when to create the first "sub" entity in an Axon Aggregate

(将此讨论从 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

The creation of the Entity takes place in an event sourcing handler of it’s parent. It is thus not possible to have a ‘command handling constructor’ on the entity class as with the aggregate root.

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

但是when/where你会创建初始实体吗?

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

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*/);
 }

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

Creating the entity should be done using the @EventSourcingHandler, in your example option b. The state changes should happen here, and the first Event Handler should set the Aggregate Identifier. Here’s a reference to the Aggregate documentation.

Hope this helps with the aggregate creation entity.

但我想知道这是否不会混淆来自@EventSourcingHandler 的实体的实例化+添加,用于创建实体 的事件(这确实看起来是正确的),与我的选项 b 在处理程序中为创建 Aggregate

事件创建第一个实体的情况

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

来自:Command Handlers - Axon Reference Guide

Axon will ignore the apply() invocation when replaying historic events upon sourcing the given Aggregate

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

c也是。正确答案?

具体来说: 如果您想在多实体聚合中创建初始实体,请在处理 聚合 创建事件的事件(采购)处理程序中创建它

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