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

时间:2017-09-13 12:01:14

标签: domain-driven-design cqrs domain-events

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

我的考虑因素(可以跳过):

  • 更新聚合状态A的命令处理程序技术上可以从存储库中获取聚合B,对其进行调用计算并将结果放入域事件中;但是我认为,即使是为了阅读目的,获取除被修改的聚合之外的聚合也不是命令处理程序的工作;也不是命令处理程序执行计算只是为了发送事件而不是修改域的状态。
  • 聚合A引发的域事件('聚合更新')仅包含聚合A的更新状态,关于聚合B的状态的信息不足。读取模型的事件处理程序无法访问到域模型,因此既不能获取聚合B也不能在聚合B上调用所需的函数来更新读取模型。
  • 我知道命令所需的任何状态都必须与命令一起传递。这样,应用程序服务在发送命令之前可以获取聚合B的状态(来自读取模型),并将其放入命令中。为此我必须将函数从聚合B移动到某个服务并传递A和B的状态。这将使聚合B更加贫血。加上上面提到的在命令处理程序中进行计算的问题。
  • 我读过有人建议只读模型的任何计算都属于读模型本身。因此,我的事件的读取模型的处理程序将具有执行计算所需的所有状态和行为。但是,这意味着我必须在查询端复制大部分域模型概念;拥有一个完整的阅读模型会太复杂。

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

请注意,以防万一我没有使用Event Sourcing。

对此情况的任何想法都将非常感激。谢谢!

更新: 让情况更具体

我的聚合是Worker s(聚合B)和Group个工作者(聚合B)。工人和团体是多对多的关系。想象一下,Group和Worker都有一些Value属性。 Worker calculateValue()是Worker的值加上Worker参与的所有组的值的函数。上述Command正在修改某些Group的Value。因此,参与该组的所有工人将返回calculateValue()的不同结果。

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

1 个答案:

答案 0 :(得分:2)

  

更新聚合状态A的命令处理程序技术上可以从存储库中获取聚合B,对其进行调用计算并将结果放入域事件中;但是我认为,即使是为了阅读目的,也不是命令处理程序的工作来获取除被修改的聚合之外的聚合;它也不是命令处理程序的工作来执行计算只是为了发送事件而不是修改域的状态。

这不行,因为事件应代表单个聚合发生的事实。

  

我知道命令所需的任何状态都必须与命令一起传递。这样,应用程序服务在发送命令之前可以获取聚合B的状态(来自读取模型),并将其放入命令中。为此我必须将函数从聚合B移动到某个服务并传递A和B的状态。这将使聚合B更加贫血。加上上面提到的在命令处理程序中进行计算的问题。

您不应该在事件中发送聚合状态。实际上,您不应以任何其他方式查询聚合或使用内部和私有状态,而应使用Aggregate本身。 在CQRS中,不会查询聚合。这是一个阅读模型的目的。

  

我读过的人建议只读模型的任何计算都属于读取模型本身。因此,我的事件的读取模型的处理程序将具有执行计算所需的所有状态和行为。但是,这意味着我必须在查询端复制大部分域模型概念;拥有一个完整的阅读模型太复杂了。

这是要走的路。但是,你还有什么重复? Aggregate使用该计算的结果是接受还是拒绝其任何命令?

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

如果不是,那么计算不应该留在聚合内,而只能留在读取模型中。

在CQRS中,通过将Read与Read模型分开,您可以将计算分为Write和Read,但在某些情况下,两个模型共享计算。在这些情况下,您可以在类中提取计算,并在两个模型中使用该类。