如何处理事件源聚合中的相关实体

问题描述 投票:0回答:1

我正在 ES 和 CQRS 的世界中冒险,我已经阅读了很多相关内容。不幸的是,大多数材料都没有超出基础知识,当您开始尝试将其应用到现实世界的示例时,您就会开始遇到示例中未涵盖的内容。

我的问题是关于相关实体的。一个非常基本的例子如下:

想象我们有产品和类别。一个产品可以有一个类别,一个类别可以有多个产品。某些类别的产品最终价格可能会增加 x%。

Product
是源自事件的聚合,它应该具有
Category
属性。但按照DDD原则思考,我只在其中保存了一个
CategoryId
Product
还有一个
CalculateFinalPrice
方法,可以获取其基本价格并添加类别中的百分比。

Category
本身可能不是聚合,但由于您在一个类别中有许多产品,因此将其与
Product
一起保存是没有意义的,因此它们必须与产品聚合分开保存。

我的问题是我何时需要从事件中重建产品。

当产品从事件中重建时,我只会有一个

CategoryId
,因为那是我保存的。但对于领域逻辑,我需要
Category

现在,如果我需要根据类别的某些状态执行一些业务逻辑,我不能。

我可以有另一个“完整”的产品模型,然后我将根据原始持久化产品构建它,对于相关实体,我必须遍历整个类别列表,找到所需的类别并在“完整”的产品。我不知道,但这种方法似乎有些不对劲。

遇到这种情况通常的做法是什么?

domain-driven-design event-sourcing
1个回答
2
投票

聚合边界由一致性边界驱动,因为这最终就是它们的全部。如果从命令(改变系统状态的意图)的角度考虑问题,事件源持久性模型中一致性的粗略定义是对于两个命令 A(导致事件 X 的集合)和 B(导致事件集合 Y) Y 将取决于 X,反之亦然。如果这两个命令具有一致性关系,那么这是一个非常强烈的信号,表明您的域希望这些命令针对相同的聚合(“想要”可以解释为,尝试使它们成为针对不同聚合的命令可能会导致很多结果)疼痛)。

因此,考虑命令时,可能会有一个

SetCategorySurchargePercentage
命令用于设置类别的百分比折扣,以及
SetProductBasePrice
命令用于设置产品的(预附加费)价格。假设我们希望
CalculateFinalPrice
SetCategorySurchargePercentage
SetProductBasePrice
保持一致,这意味着所有三个操作(
CalculateFinalPrice
可能实际上是一个查询,但一致性要求有效地将其变成只读命令)想要针对相同的聚合:类别将包含该类别中所有产品的价格信息。

这可能并不理想,这是有原因的:对于包含大量产品和/或大量价格计算的类别,排序将产生很多争议。然而,一致性要求(或者至少是我们对这些要求的理解)几乎迫使我们来到这里(应该注意的是,关系数据库中的外键约束之类的东西是隐藏这一争论点而不是消除它)。

如果有不同的一致性要求,则可以采用替代解决方案。例如,如果我们决定

SetCategorySurchargePercentage
和反映在
CalculateFinalPrice
中的新百分比之间存在延迟(甚至可能是可观察到的延迟),那么我们可以让前一个命令针对类别聚合,并从成功命令针对该类别中的产品的命令。产品聚合将包含其了解的最新百分比作为其状态的一部分。

与 CQRS 保持一致的另一种方法是通过读取模型(例如最终价格的数据库表)来回答

CalculateFinalPrice
。流程订阅类别百分比变化事件、“此产品属于此类别”事件和产品基本价格事件,并对其表进行适当的更新:请注意,如果采用此方法,现在将有一个(可能可观察到的)
SetCategorySurchargePercentage
SetProductBasePrice
之间以及
CalculateFinalPrice
反映结果时的延迟。

© www.soinside.com 2019 - 2024. All rights reserved.