Domain driven design CQRS读取模型预测:数据转换有多复杂

Domain driven design CQRS读取模型预测:数据转换有多复杂,domain-driven-design,viewmodel,cqrs,projection,Domain Driven Design,Viewmodel,Cqrs,Projection,我想在视图投影上检查一下自己的理智,看看中间概念是否可以纯粹存在于read模型中,同时提供命令之间的桥梁 让我用一个人为的例子来解释 我们下订单时会引发OrderPlaced事件。然后,该工作流涉及生成用于准备装运的领料单 可以从订单(或订单组)生成领料单,而无需任何外部来源或用户提供任何附加信息。那么,是否可以将拣货单纯粹表示为读取模型 因此: 然后,仓库经理可以查看领料单,选择他们想要装运的行,然后执行PrepareShipment命令。ShipmentPrepared事件随后将更新原始订单

我想在视图投影上检查一下自己的理智,看看中间概念是否可以纯粹存在于read模型中,同时提供命令之间的桥梁

让我用一个人为的例子来解释

我们下订单时会引发OrderPlaced事件。然后,该工作流涉及生成用于准备装运的领料单

可以从订单(或订单组)生成领料单,而无需任何外部来源或用户提供任何附加信息。那么,是否可以将拣货单纯粹表示为读取模型

因此:

然后,仓库经理可以查看领料单,选择他们想要装运的行,然后执行PrepareShipment命令。ShipmentPrepared事件随后将更新原始订单,并从PickingSlipView中删除相关行

我知道这是一个玩具示例,但我有一个概念上类似的用例,其中一位同事认为PickingSlip本身应该是一个域实体/聚合,因为它在概念上与order不同。因此,您有PlaceOrder、GeneratePickingSlip和PrepareShipment命令

但是,generatePickSlip命令只是获取订单号(标识符),将订单数据转换为拣货单实体,并保存该实体。除了使用提货单准备装运外,您不能修改或删除提货单或对其执行任何操作

这感觉就像在写模型上引入了不必要的开销,因为它最终只是对现有信息进行转换以启用另一个命令

因此(不深入探讨仓库和航运的问题空间)

我提出的是一个合法的阅读模型用例吗

充当两个命令之间的中介,通过将某些数据转换为不同的视图。或者,正如我的同事所建议的,在所有情况下,每个概念都应该在write模型中表示吗

我觉得我的方法更简单,避免了不必要的复杂性,但我对CQR还不熟悉,所以可能遗漏了一些东西

编辑-备选示例

提供另一个示例来探索:

我们有一个分类记录簿,其中每个记录都是关于产品及其位置的信息。记录簿由外部系统填充,包含SKU编号,映射到可用位置:

Book of Record (Electronics)
SKU#    Location1     Location2    Location3   ...    Location 10
XXXX    Introduce     Remove       Introduce   ...    N/A
YYYY    N/A           Introduce    Introduce   ...    Remove
每本记录簿都是一个实体,每一行都是一个值对象

记录簿用于生成不同的任务(在要分配给某个人的任务计划中分组)。该计划可能仅涵盖一部分位置

有不同类型的任务:一个任务计划用于在某个位置添加或从货架上移除库存的个人。将此任务称为AllocateStock任务。管理多个地点的区域主管可以执行另一种任务,即检查货架是否正确地遵循了商店的指导原则,如检查显示任务。对于库存分配,我们对引入和删除的SKU都感兴趣。为了检查显示,我们只对新推出的SKU等感兴趣

我们正在探讨两种选择:

选项1

创建任务的人有一个视图(读取模型),允许他们选择记录簿。说他们选择电子产品和时尚。然后,他们选择一个或多个位置。然后,他们可以提交如下命令:

GenerateCheckDisplayTasks(TaskPlanId, List<BookOfRecordId>, List<Locations>)
生成任务时,该视图允许用户选择要为其生成任务的类别和位置。也许他们选择了电子产品类别和位置1和位置3

命令现在是:

GenerateCheckDisplayTasks(TaskPlanId,  List<BookOfRecordId, SKU, Location>)
GenerateCheckDisplayTasks(任务计划ID,列表)
此时,该命令不再负责过滤出位置、移除和不适用项等所需的逻辑

因此,第一个选项的命令只提交正在转换为任务的实体的ID以及筛选器选项,并在内部完成所有工作,可能是利用域服务

第二个选项将过滤方面卸载到视图模型,现在命令提交将生成任务的值

注意:根据聚合不应凭空出现的指导,任务计划聚合将创建任务

我试图确定选项2是否将太多的责任推到了读取模型上,或者这种过滤行为是否更适用于那里


抱歉,我试图使用PickingSlip示例,因为我认为这将是一个更容易识别的问题空间,但现在我意识到,这一概念所包含的含义可能已经把问题弄得一团糟。

在我看来,您问题的答案在很大程度上取决于您如何设计域,而不是如何实现CQR。按照您的表示方式,似乎所有这些操作和聚合都在同一个有界上下文中,但乍一看,我认为有3个(命名很困难!):

  • 订单管理或销售,在其中下订单
  • 仓库操作,货物包装后装运
  • 装运,将包裹放入卡车并离开
  • 在订单管理中下订单时,仓库会做出反应并启动打包工作流。此时,仓库应该拥有执行其逻辑所需的所有数据,而不再需要
    订单

    然后,仓库经理可以查看领料单,选择他们想要装运的行,然后执行PrepareShipment命令

    对我来说,这清楚地表明需要一个集合来确保不变量得到尊重。不能选择拣货单中不存在的项目,不能选择超过指定数量的项目,不能选择
    Category                       SKU     Location
    Electronics (BookOfRecordId)   XXXX    Location1
    Electronics (BookOfRecordId)   XXXX    Location3
    Electronics (BookOfRecordId)   YYYY    Location2
    Electronics (BookOfRecordId)   YYYY    Location3
    Fashion     (BookOfRecordId)   ...     ... etc
    
    GenerateCheckDisplayTasks(TaskPlanId,  List<BookOfRecordId, SKU, Location>)