我应该如何处理多个聚合根交互

问题描述

我读过这篇post在这篇博文中 Udi Dahan 谈到了多对多的关系。 在那个例子中,他解释说,在多对多关系的情况下,就像工作与工作委员会之间的关系,并考虑到将工作添加到工作委员会的有界上下文,聚合根将是工作委员会和您只需将作业添加到其中即可。 在评论部分,他还解释说,在不同的有界上下文中,作业将是聚合根,这是有道理的,因为作业可以在没有作业板的情况下存在,并且您可以对不影响作业的作业执行许多操作木板。 我有一个类似的问题,但我似乎无法弄清楚这是如何解决的。我有两个问题:

  1. 如果我们需要删除工作,这取决于工作是否已发布,我们需要单独删除工作或删除工作但同时将其从董事会中删除,这将意思是修改一个事务中的两个聚合根,但是这个代码应该去哪里呢?域服务?
  2. 如果工作和工作板可以是两个不同的聚合,工作实体需要存在于两种上下文中,那么我们如何处理这个问题,只需创建两个具有重复数据的工作类?

更新 1:

这就是我正在处理的场景......我有一个路由应用程序,我有代表行程请求的请求,我有路线,其中有停靠点,每个停靠点都有一个或多个请求。为了创建路由,我使用了一个执行路由的外部服务,并将路由结果存储在路由表中。

问题是我不知道如何建模这种关系,这里有一个用例要考虑,请求取消是一个过程,根据请求的状态和路由的状态可以导致不同的动作:

  1. 请求未路由(未分配给路由),只需取消请求即可。
  2. 请求被路由,路由被调度,然后取消请求,从路由中删除请求,并重新创建路由(使用外部库),因为删除请求可能导致删除停止,所以我需要重新创建路由内部,它仍然是一个更新。
  3. 请求已路由,路由在路由中,然后我将请求标记为未显示,并更新路由。

所以起初我认为请求、路由和路由表是单独的聚合,但这意味着我需要在同一个事务中修改多个聚合,(通过使用服务或使用域事件) 我不确定创建更高级别的聚合根(带有请求和路由数据,并最终路由数据)是否有意义,因为我不会总是拥有加载聚合根的所有数据,事实上大部分我将拥有其中的一部分的时间,无论是 1 个请求还是具有多个请求的路由。 我愿意接受建议,因为我似乎无法解决这个问题。

更新2: 因此,添加更多上下文,我将为实体添加更多细节:

  • 请求,它代表一个旅行请求,它有几个状态,并定义了工作流程
  • Route,它有一个定义好的工作流和定义的转换,有一个停靠点的集合,每个停靠点都有一个有效载荷的集合,每个有效载荷都有一个请求 id,(Route ->stops[] -> payloads[]-> requestId)
  • 路由,它代表调用路由引擎的结果,它根据你要路由的一系列请求生成路由/路由

所以这个实体存储在一个 mongodb 集合中,让我们看看用例:

UC - 请求取消 我只能使用请求 id 取消请求,但根据请求的状态,我可能还需要修改路由。 1 请求未路由,因此使用请求 ID,我获取请求并取消它,这很简单。 2 Request is Routed,Route is Scheduled,在这种情况下,我需要获取请求,然后获取路由以及与该路由相关的所有请求(包括触发命令的请求),然后删除有效负载(和停止(如果它只有一个有效负载)与请求相关联,因为此选项可以更改停止,我需要使用外部 api(路由引擎)重新创建路由并在路由表中创建一个条目。 3 Request is Routed,Route is en-route,在这种情况下,我需要获取请求,然后获取路由以及与该路由相关的所有请求(包括触发命令的请求),并更改请求为未出现,但也将有效负载标记为未出现

UC - 开始路线 一旦创建并调度了路由,我就可以启动它,这意味着修改路由状态、停止状态、负载状态和相关请求的状态。

正如你在用例中看到的,路由 - 请求和路由表,非常密切相关,所以起初虽然有单独的聚合根,请求是一个 AR,路由是一个 AR,路由是一个 AR,但是这意味着在同一笔交易中修改多个 AR。 现在让我们看看拥有所有实体的 AR 是什么样子

class Aggregate {
  constructor(routeData,requests[]) {
  }
}

那么让我们再看看 UC UC - 请求取消

  1. 在这种情况下,我只有 1 个请求数据,所以我必须将 routeData 留空,这听起来不对
  2. 在这个中,我有路线和请求数据,所以我很好
  3. 在这个中,我有路线和请求数据,所以我很好

这里的主要问题是,有些操作可以在1个请求上完成,有些操作会在路由上完成,有些则在两者中都完成。所以我不能总是通过Id获取聚合,或者我不能总是用相同的id获取它。

解决方法

  1. 没有“同一事务中的两个聚合根”这样的东西。事务范围在单个聚合内,因为理论上所有聚合都应该存在于它们自己的微服务中。以原子方式更新两个或多个聚合的正确方法是使用 saga。传奇是一个复杂/高级的话题。如果可以,我建议您重新考虑您的设计,避免使用它们。

  2. 在两个有界上下文之间拆分实体非常好,而且大部分时间都是必要的,但是这些实体应该适应它们的上下文,例如在板限界上下文中,“工作”实体可以是“板卡”,它与来自工作限界上下文的“工作”实体具有不同的属性。