如何用 DDD/CQRS/ES 建模仓库应用程序?
How to model a warehouse application with DDD/CQRS/ES?
我们要为仓库应用程序建模。假设我们确定了以下真实世界对象:
- 文章(仓库里存放的东西)
- 调色板(文章所在的位置)
- 隔间(货架中存放调色板的地方)
存在以下限制条件:
- 一个调色板正好在一个隔间里
- 一个隔间可以容纳零个或一个调色板
一开始我们有一个操作:
- 移动(将调色板从当前隔间移动到另一个隔间)。
当然这是非常简化的。
应该如何建模?
我认为 Stockitem 可能是一个价值对象。一种解决方案是将整个仓库建模为具有调色板和隔间实体的集合体。在这种情况下,关于其约束(不变量)的移动操作可以毫无问题地实施。但这种方法有明显的缺点。此聚合的事件日志将无限增长。由于聚合版本控制等原因,无法并行执行两个移动操作。而且从ddd的角度来说,我觉得不太合适。
另一种方法是使每个调色板和每个隔间都有自己的集合。那么move操作如何实现呢?
问题 1:谁加载引用的聚合?
我认为它应该坚持调色板。调色板可以引用它所在的隔间(它是一个聚合体)。但是这个引用是如何实现的(CQRS/ES)?移动命令的 Comandhandler 显然将从调色板存储库加载调色板聚合并调用其上的移动方法。谁加载引用的隔间?谁装载它应该移动到的隔间?我读到聚合不应访问存储库。 commandhandler 是否应该加载两个隔间?隔间是否应该作为参数提供给 move 方法?或者 commandhandler 是否应该将当前隔间设置为调色板并将目标隔间作为参数?
问题2&3:聚合之间的约束和双向关联
约束条件如何?要检查目标隔间是否为空,隔间需要知道存储在其中的调色板。这是应该避免的双向关联。而且因为它们是不同的聚合,它们不能在同一个事务中更新。调色板是否触发域事件以通知隔间它将移动到它?它必须作为具有撤消操作的传奇来实现吗?如果两个动作冲突,一个获胜,但是由于旧隔间已满,所以无法将松散的调色板移回原处怎么办?
对于一个非常简单的问题,这一切对我来说似乎都非常复杂。
在书籍和示例中,一切似乎都很清楚。但是如果我尝试使用它,我似乎做错了。
有人可以指导我正确的方向吗?
您应该只开始对行为进行对象建模,根本不要考虑聚合和值对象。一旦您对所需的行为进行建模,您就会知道什么是实体、聚合、根和值对象。
在 Vaughn Vernon 的解释中,他明确表示,在做出这些决定之前,您需要弄清所需的细节。
问题 1
我认为除了这里的业务分析,你还需要做一些交易分析。
如果您的域是高度协作的(这是 DDD 推荐的),移动到给定隔间的频率如何?在跨越 2 Compartment
聚合(源和目标)的事务下发生 Move 操作是否可行?或者最终的一致性是否足够,源 Compartment 将通过调色板离开它的事件向世界发出信号,而目标 Compartment 稍后会以某种方式以异步方式通知调色板正在加入它?
调色板浮动 "in limbo" 一小段时间是完全可以接受的,这是您需要咨询领域专家的问题。
问题2
双向关联不是唯一的解决方案。您可以向 PaletteRepository
询问所有具有 CompartmentID X 的 Palettes。或者,Compartment 可以(应该)有一个 Palette ID 列表,而不是对它们的完整引用。
总的来说,我认为您应该先看看您问自己的所有设计问题是否有业务implication/answer。领域专家通常会对最终一致性是否现实或是否需要立即一致性、发生冲突时应该发生什么等问题发表有根据的意见。
我们要为仓库应用程序建模。假设我们确定了以下真实世界对象:
- 文章(仓库里存放的东西)
- 调色板(文章所在的位置)
- 隔间(货架中存放调色板的地方)
存在以下限制条件:
- 一个调色板正好在一个隔间里
- 一个隔间可以容纳零个或一个调色板
一开始我们有一个操作:
- 移动(将调色板从当前隔间移动到另一个隔间)。
当然这是非常简化的。
应该如何建模?
我认为 Stockitem 可能是一个价值对象。一种解决方案是将整个仓库建模为具有调色板和隔间实体的集合体。在这种情况下,关于其约束(不变量)的移动操作可以毫无问题地实施。但这种方法有明显的缺点。此聚合的事件日志将无限增长。由于聚合版本控制等原因,无法并行执行两个移动操作。而且从ddd的角度来说,我觉得不太合适。
另一种方法是使每个调色板和每个隔间都有自己的集合。那么move操作如何实现呢?
问题 1:谁加载引用的聚合?
我认为它应该坚持调色板。调色板可以引用它所在的隔间(它是一个聚合体)。但是这个引用是如何实现的(CQRS/ES)?移动命令的 Comandhandler 显然将从调色板存储库加载调色板聚合并调用其上的移动方法。谁加载引用的隔间?谁装载它应该移动到的隔间?我读到聚合不应访问存储库。 commandhandler 是否应该加载两个隔间?隔间是否应该作为参数提供给 move 方法?或者 commandhandler 是否应该将当前隔间设置为调色板并将目标隔间作为参数?
问题2&3:聚合之间的约束和双向关联
约束条件如何?要检查目标隔间是否为空,隔间需要知道存储在其中的调色板。这是应该避免的双向关联。而且因为它们是不同的聚合,它们不能在同一个事务中更新。调色板是否触发域事件以通知隔间它将移动到它?它必须作为具有撤消操作的传奇来实现吗?如果两个动作冲突,一个获胜,但是由于旧隔间已满,所以无法将松散的调色板移回原处怎么办?
对于一个非常简单的问题,这一切对我来说似乎都非常复杂。
在书籍和示例中,一切似乎都很清楚。但是如果我尝试使用它,我似乎做错了。
有人可以指导我正确的方向吗?
您应该只开始对行为进行对象建模,根本不要考虑聚合和值对象。一旦您对所需的行为进行建模,您就会知道什么是实体、聚合、根和值对象。
在 Vaughn Vernon 的解释中,他明确表示,在做出这些决定之前,您需要弄清所需的细节。
问题 1
我认为除了这里的业务分析,你还需要做一些交易分析。
如果您的域是高度协作的(这是 DDD 推荐的),移动到给定隔间的频率如何?在跨越 2 Compartment
聚合(源和目标)的事务下发生 Move 操作是否可行?或者最终的一致性是否足够,源 Compartment 将通过调色板离开它的事件向世界发出信号,而目标 Compartment 稍后会以某种方式以异步方式通知调色板正在加入它?
调色板浮动 "in limbo" 一小段时间是完全可以接受的,这是您需要咨询领域专家的问题。
问题2
双向关联不是唯一的解决方案。您可以向 PaletteRepository
询问所有具有 CompartmentID X 的 Palettes。或者,Compartment 可以(应该)有一个 Palette ID 列表,而不是对它们的完整引用。
总的来说,我认为您应该先看看您问自己的所有设计问题是否有业务implication/answer。领域专家通常会对最终一致性是否现实或是否需要立即一致性、发生冲突时应该发生什么等问题发表有根据的意见。