【问题标题】:How to model a warehouse application with DDD/CQRS/ES?如何使用 DDD/CQRS/ES 对仓库应用进行建模?
【发布时间】:2015-05-27 09:08:37
【问题描述】:

我们想要为仓库应用程序建模。让我们假设我们确定了以下现实世界的对象:

  • 物品(仓库存放的东西)
  • 调色板(文章所在的位置)
  • 隔间(货架上存放调色板的地方)

有以下限制:

  • 调色板恰好在一个隔间中
  • 一个隔间可以容纳零个或一个调色板

首先我们有一个操作:

  • 移动(将调色板从其当前隔间移动到另一个隔间)。

当然这是非常简化的。

这应该如何建模?

我认为 Stockitem 可能是一个价值对象。一种解决方案是将整个仓库建模为具有调色板和隔间实体的聚合体。在这种情况下,关于其约束(不变量),可以毫无问题地实现移动操作。但是这种方法有明显的缺点。此聚合的事件日志将无限增长。由于聚合版本控制等原因,两个移动操作无法并行执行。而且从 ddd 的角度来看,这对我来说感觉不合适。

另一种方法是让每个调色板和每个隔间都有自己的聚合。但是那移动操作怎么实现呢?

问题 1:谁加载引用的聚合?

我认为它应该坚持使用调色板。调色板可以引用它所在的隔间(它是一个聚合)。但是这个参考是如何实现的(CQRS/ES)? move 命令的 Comandhandler 显然将从调色板存储库加载调色板聚合并在其上调用 move 方法。谁加载引用的隔间?谁来装载它应该移动到的隔间?我读到聚合不应该访问存储库。指挥员应该装载两个隔间吗?隔间是否应该作为参数提供给 move 方法?或者命令处理程序是否应该将当前隔间设置为调色板并将目标隔间作为参数?

问题 2 和 3:聚合之间的约束和双向关联

约束呢?要检查目标隔间是否为空,隔间需要知道存储在其中的调色板。这将是应避免的双向关联。并且因为它们是不同的聚合,它们不能在同一个事务中更新。调色板是否有触发域事件以通知隔间它将移动到它?它是否要作为具有撤消操作的 saga 来实现?如果两招冲突,一招胜出,输掉的棋盘却无法挽回,因为此时旧车厢已满?

对于这个非常简单的问题,这一切对我来说似乎都很复杂。

在书籍和示例中,一切似乎都如此清晰。但是如果我尝试使用它,我似乎做错了。

有人可以指导我正确的方向吗?

【问题讨论】:

    标签: domain-driven-design cqrs event-sourcing


    【解决方案1】:

    问题 1

    我认为除了这里的业务分析之外,您还需要做一些事务分析。

    如果您的域是高度协作的(推荐使用 DDD),移动到给定隔间的频率如何?在跨越 2 个 Compartment 聚合(源和目标)的事务下发生移动操作是否可行?或者最终的一致性就足够了,源隔间将通过调色板离开它的事件向世界发出信号,而目标隔间稍后会以异步方式以某种方式被告知调色板正在加入它?

    调色板在短时间内“陷入困境”是完全可以接受的,这是您需要询问领域专家的问题。

    问题 2

    双向关联并不是唯一的解决方案。您可以向 PaletteRepository 询问所有具有 CompartmentID X 的 Palettes。或者,Compartment 可以(应该)有一个 Palette ID 列表,而不是对它们的完整引用。

    总的来说,我认为你应该先看看你问自己的所有设计问题是否有商业含义/答案。领域专家通常会对最终一致性是否现实或是否需要立即一致性、在发生冲突时应该发生的情况等有有根据的意见。

    【讨论】:

    • 第二种解决方案的问题在于,您仍然不能强制 Compartment 不包含多个 Palette,反之亦然,具体取决于关系方面。我经常想知道是否可以仅依靠数据库约束来执行这些规则。例如,如果您在Palette 上按住compartmentId 并执行palette.moveTo(compartment),您可以有效地确保调色板永远只有一个隔间,但不能确保隔间只有一个调色板。然而,依靠数据库约束来强制执行该规则是微不足道的,并且可以让您保持高度一致。
    • @plalx 我想我希望这个不变量在代码中明确并在Compartment 中强制执行,这要归功于它的内部调色板计数,或者如果它没有,有人必须传递它(或者Compartment 可以直接调用 Repo 来获取它)
    • 是的,我知道这一点。所以你认为最终一致性的“传奇”解决方案是正确的方式吗?我的问题 1 问题的另一部分是,谁加载了所需的聚合。我会稍微澄清一下这个问题。
    • 作为您对问题 2 的回答,如果我使用 (cqrs with) 事件源,则没有简单的方法可以查询存储库中所有具有隔间 ID x 的调色板。这只有在读取模型(保存此信息的投影)中才有可能。但是写入模型的聚合如何从读取模型的投影中获取信息?
    • 我不认为有一个正确的方式,正如我解释的那样,只有你才能知道哪种方式更适合你的领域:)
    【解决方案2】:

    您应该只是开始对行为进行对象建模,而根本不要考虑聚合和值对象。一旦您对所需的行为进行了建模,您就会知道实体、聚合、根和值对象是什么。

    Vaughn Vernon 的解释中,他清楚地表明,在做出这些决定之前,你需要弄清楚你需要什么的细节。

    【讨论】:

    • 你当然是对的。在我的示例中,唯一的行为是 move 方法。与此相关的实体和集合是什么?我的部分问题是它不是一个纯粹的 ddd 问题,而是关于使用 cqrs/es 的实现细节。
    • 只要你不添加超过这个移动,单个聚合就可以了。物理移动很慢,所以谁在乎平行移动,只需将它们序列化即可。更多的行为,我们想要更多的行为!
    猜你喜欢
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 2015-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多