【问题标题】:Why is EventSourcingHandler (in aggregate object) needed?为什么需要 EventSourcingHandler(在聚合对象中)?
【发布时间】:2019-02-28 20:25:16
【问题描述】:

公平警告:我不知道自己在做什么,所以即使提出这个问题也可能会出错。

我想更新一个简单对象(聚合)的状态,然后为 UI 提供更改后的对象的投影。这是我的聚合对象(命令处理程序存在,但此处未显示)。

@Aggregate
public class Widget {
    @AggregateIdentifier
    private String id;
    private String color;
...
    @EventSourcingHandler
    public void on(ChangeColorEvt evt) {
        color = evt.getColor();
    }
... 
}

...这是我的投影:

public class WidgetProjection {
    private final EntityManager entityManager;
    private final QueryUpdateEmitter queryUpdateEmitter;
...
    @EventHandler
    public void on(ChangeColorEvt evt) {
        ProjectedWidget projection = entityManager.find(ProjectedWidget.class, evt.getId());
        projection.setColor(evt.getColor());
        queryUpdateEmitter.emit(FetchWidget.class, query -> evt.getId().startsWith(query.getFilter().getIdStartsWith()), projection);
    }
...
}

QueryHandler 执行您所期望的操作,找到实例并返回它。

所以这是我的问题:

  1. 为什么我需要聚合中的 EventSourcingHandler?这就对了 似乎可以工作,但结果并未存储或发送到任何地方。
  2. 所以,这成为我的下一个问题:在执行 EventSourcing 之后 处理程序,是 Widget 的结果实例(不是投影) 存储、查看或发送到任何地方?
  3. 如果确实需要EventSourcingHandler,有什么办法可以避免 拥有我的业务逻辑的两个独立副本(一个在 EventSourcingHandler 和一个 EventHandler)?我真的很讨厌这个主意 不必在两个地方更新相同的业务逻辑。

我很欣赏该小组可以提供的任何清晰度。感谢您的帮助!

【问题讨论】:

  • @Allard,我希望您或您团队中的某个人会看到这一点。我很感激任何帮助。谢谢。
  • 我可以向你保证,他团队中的某个人已经看到了。我希望我可以为你澄清一些事情@Jonathan M :-)

标签: java axon


【解决方案1】:

希望我能帮助你解决这个问题!

简短的回答是: 使用@EventSourcingHandler 的必要性实际上取决于您希望如何为您的应用程序建模。

现在,这没什么好说的,所以让我详细说明一下我为什么要这么说。 在回答您的问题时,我假设您希望创建一个应用程序,您可以在其中遵循 DDD、CQRS 和事件溯源的想法。因此,Axon 在构建应用程序时试图为您简化的概念。

让我从这里复习一下你的编号问题:

  1. 在聚合中需要 @EventSourcingHandler 注释函数的原因是 (1) 根据 it 已发布的 (2) 事件更新聚合状态。转到第 1 点,为什么要更新聚合的状态?由于您的聚合是应用程序的命令模型,它的任务是根据收到的命令做出决策。为了能够做出决定,有时聚合需要状态,但有时不需要。因此,在您分享的Widget 示例中,我假设color 字段稍后根本不用于驱动业务逻辑,因此您可以完全从聚合中省略此状态而不会出现任何问题。在我回复的第二点中,我试图指出聚合只会处理源自自身的事件。这是因为事件是聚合的来源,因为事件构成了在该给定模型上发生的所有增量。
  2. 您的以下问题非常适合继续我在第 1 点开始的解释。答案很简单,您的 Widget 聚合没有存储在任何地方,而不是原样。每次您从Repository(在 Axon 中为您自动完成)检索聚合时,在 Axon 中默认为 EventSourcingRepository,该给定聚合的所有事件都将从事件存储中检索。然后,创建一个 empty Aggregate 实例,并且框架将重播它为该确切 Aggregate 实例找到的所有事件。因此,每次有新命令进入时,您都可以有效地对聚合进行事件溯源。这听起来可能有点矫枉过正,因为给定聚合的事件数量可能会增长到相当大的集合。这可以通过将 snapshots 设置为聚合来解决。
  3. 如果这种形式将您的应用程序拆分为一个专门处理业务逻辑的部分,即命令模型,以及仅返回一个查询模型作为答案的部分,即查询模型,那么您可以决定使用 @ 987654322@。所以请注意,在使用 Axon 时,您根本不需要进行事件溯源;这只是框架的默认作案手法。因此,我理解您的担忧,即您在重复自己的逻辑。但是,您可以严格区分做出所有决定的部分,这些决定将保留在您的聚合中。 查询模型(在您的示例中为ProjectedWidget)可以以您想要的任何格式和任何数据库/工具存储,理想情况下没有任何业务逻辑。 如果您确实发现自己在应用程序的查询端添加了业务逻辑,这可能建议您应该将此位升级为源自您的聚合的事件。

我希望这能让您深入了解为什么要开始使用事件溯源。这篇文章描述了CQRS 比我在这里能做的更详细一点,以及this 事件溯源链接;希望它们可以作为比我刚才给你的解释更彻底的解释。

【讨论】:

  • 啊,所以 EventSourcingHandlers (ESH) 中的状态更改只是为了启用 CommandHandlers (CH) 中的决策逻辑,对吧?而且,如果在后面的 CH 决策中不使用 ESH 状态更改,则可以/应该消除 ESH,对吗?相反,状态应该只在查询中的 EventHandler (EH) 中改变,对吧?
  • 不过,在需要ESH的情况下,EH的状态变化基本与之相同。如果状态变化涉及多个属性的计算,则有可能出错:可能计算在 ESH 中编码正确,但在 EH 中编码不正确。我们创造了一个糟糕的情况,因为我们有基本相同的计算,但在两个地方。有没有办法避免这种情况?
  • 嗨 Jonathan,避免这种情况的最佳方法是在命令模型中做出重要的决策和计算,而在视图模型中只留下琐碎的计算。不过要小心不要过度。建模是一种很好的平衡行为......
  • @JonathanM 如果您需要避免在域特定计算方面的重复,我认为您可以将它们放在域服务中。从我的角度来看,我会让该服务接受计算所需的所有字段并每次返回相同的结果;甚至可以使它成为一个带有静态计算函数的抽象类。
  • @Allard,啊,我想你是说将繁重的计算放在 CH 中并将结果传递给 ESH/EH,以便那些处理程序可以更新状态,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多