首页 > 解决方案 > 维护聚合之间的引用

问题描述

我试图围绕如何维护两个聚合之间的 id 引用,例如。当任何一方发生影响关系的事件时,另一方也会以最终一致的方式进行更新。

在节日的上下文中,我有两个聚合,一个用于“团队”,一个用于“事件”,代码如下:

@Aggregate
public class Event {
    @AggregateIdentifier
    private EventId eventId;
    private Set<TeamId> teams; // List of associated teams

... protected constructor, getters/setters and command handlers ...
}
@Aggregate
public class Team {
    @AggregateIdentifier
    private TeamId teamId;
    private EventId eventId; // Owning event

... protected constructor, getters/setters and command handlers ...
}

团队必须始终与事件相关联(通过 eventId)。一个事件包含相关团队的列表(通过团队 ID 集)。在 Team 聚合上创建团队 (CreateTeamCommand) 时,我希望使用新创建的团队的团队 ID 更新 Event 聚合上设置的 TeamId。如果执行事件聚合上的命令“DeleteEventCommand”,则与该事件关联的所有团队也应被删除。如果团队从一个事件移动到团队聚合上的另一个事件 (MoveTeamToEventCommand),则应更新团队聚合上的 eventId,但应从旧事件聚合中删除 TeamId,并将其添加到新事件聚合中。

我目前的想法是创建一个传奇,我将SagaLifecycle.associateWith在“CreateTeamCommand”(基本上是关系第一次开始)上为 Event 聚合上的 eventId 和 Team 聚合上的 teamId 运行一个 @StartSaga,然后有一个事件影响关系的每个事件的处理程序。我对这个解决方案的主要问题是:

1:这意味着我将为每个可能的团队和事件组合都有一个独特的传奇。如果将其缩放到例如,这是否会导致性能问题。100 万场赛事,每场赛事有 50 支球队?(这对于这种情况是不现实的,但与维护聚合之间关系的一般解决方案相关)。

2:这将需要我有自定义命令和事件处理程序专门用于处理事件聚合的团队列表中的团队更新,因为不应在 saga 中处理结果事件以避免更新引用的无限循环。

感谢您阅读这个小故事,我希望有人可以确认我走在正确的轨道上,或者为我指明正确解决方案的方向。

标签: cqrsevent-sourcingaxon

解决方案


一个事件包含相关团队的列表(通过团队 ID 集)。

如果您在此处通过“事件”来表示“事件聚合”,我认为您的事件聚合不需要团队 ID。如果您认为确实如此,那么了解您对此的推理会很棒。

我认为您需要的是通过您的阅读方了解这一点。您对单个“事件”的读取模型可以监听所有其他“事件”相关事件,CreateTeamCommandMoveTeamToEventCommand相应地构建投影。请记住,不要在设计聚合时考虑到查询问题

如果执行事件聚合上的命令“DeleteEventCommand”,则与该事件关联的所有团队也应被删除。

这里有几件事:

  • 同样,您的阅读方可以收听此事件,并相应地更新预测。
  • 您还可以开始对Team聚合的相关命令处理程序执行验证,以在执行操作之前检查是否Event存在。这不会完全同步,但会涵盖大多数情况(请参阅“我如何在下订单时验证客户 ID 是否确实存在?”部分
  • 如果您真的想Team从事件的后面删除关联的聚合DeleteEventCommand,您需要在Saga中处理它,因为您无法以原子方式执行此操作而不会泄露数据存储系统的细节进入您的域模型。因此,您需要一定的重试和幂等性需求,而 saga 可以为您提供。这不完全是您在这里的建议,但相关的事实是单个命令不能作用于一组聚合,请参阅“如何使用单个命令更新一组聚合?” 部分在这里

推荐阅读