应用命令后调用交叉聚合计算函数更新读取模型

Calling cross-aggregate calculation function to update read model after a command applied

我是 CQRS 的新手,需要针对我设计中的以下情况提供建议。命令更新聚合 A 的状态;读取模型需要用交叉聚合计算方法的结果进行更新;此方法属于另一个聚合 B,它持有对聚合 A 的引用;该方法是聚合 B 和引用的聚合 A 的状态函数。调用此函数的正确位置在哪里?

我的考虑(可跳过):

我刚刚想到了以下解决方案:在域内,创建域事件“Aggregate A updated”的处理程序。它会获取聚合 B,对其调用计算方法,然后引发“聚合 B 函数结果已更改”事件,其中包含新的计算结果。然后读取模型能够从这个事件中获取结果并更新自身。这样可以吗?

请注意以防万一我没有使用事件溯源。

如果您对这种情况有任何想法,我们将不胜感激。谢谢!

更新:使情况更具体

我的聚合是 Workers(聚合 B)和 Groups 工人(聚合 B)。 Worker 和 Groups 是多对多的关系。想象一下,一个 Group 和一个 Worker 都有一些 Value 属性。 Worker 的 calculateValue() 是 Worker 的值加上该 Worker 参与的所有组的值的函数。上述命令正在修改某些组的 Value。结果,所有参与该组的 Worker 将 return 不同的结果 calculateValue()

我想从读取模型中得到什么?我想要一个具有计算值的工人列表(已经考虑了工人所有组的值)。我什至不需要 Group 在阅读方面。如果我采用“在读取端进行计算”的方式,我需要组以及那里的整个关系结构。恐怕这会是一个不合理的并发症。

Command handler updating state of aggregate A could technically fetch aggregate B from the repository, call calculation on it and put result in the domain event; however I believe it's not command handler's job to fetch aggregates other than one being modified, even for reading purposes; also it's not command handler's job to perform calculations just to send with events rather than modify the state of domain.

这不行,因为事件应该代表与单个聚合有关的事实。

I know that any state needed by command which is external to the aggregate being modified must be passed along with the command. This way the application service, before sending the command, could fetch state of aggregate B (from read model), and put it in the command. For that I would have to move the function from aggregate B to some service and pass there states of both A and B. That would make aggregate B more anemic. Plus the above mentioned problem with doing calculations within command handler.

您不应在事件中发送聚合状态。事实上,您不应该查询聚合或以任何其他方式而不是通过聚合本身使用它 internal 和 private 状态。 在 CQRS 中不查询聚合。这就是阅读模型的目的。

I've read people suggesting that any calculations that only read model is interested in belong to the read model itself. So the read model's handler of my event would just have at its disposal all needed state and behavior to perform calculations. However that would mean I have to duplicate much of the domain model concepts at the query side; it would be too complex to have a full-blown read model.

这是要走的路。然而,你到底复制了什么?聚合是否使用该计算的结果来接受或拒绝其任何命令?

如果是,那么它应该在聚合内部完成,在命令执行时并且可能最终结果随事件一起发送,但前提是可以使用来自命令 and/or 内部聚合状态的数据,而不是交叉聚合状态。如果一个聚合需要来自其他聚合的数据,那么这表明您的聚合边界可能是错误的。

如果不是,则计算不应保留在聚合内部,而应仅保留在读取模型中。

在 CQRS 中,通过将写入模型与读取模型分开,您也可以将计算拆分为写入和读取,但在某些情况下,两个模型共享一个计算。在这些情况下,您可以在 Class 中提取计算并在两个模型中使用 Class。