1

我正在尝试使用域驱动设计规则和模式开发我的第一个应用程序。

在我的业务场景中,我必须管理客户列表并跟踪发送到特定客户目的地的所有包裹。因此,按照规则,事实证明:

  • 客户是一个实体,也是一个 AggregateRoot。
  • 目的地是一个实体,它是客户的孩子(因为没有客户就没有意义)
  • 包是一个实体。它也是另一个 AggregateRoot,因为它是一个不同的事务边界,我使用 AddDestination(string destinationName ...) 方法开发了客户聚合,该方法负责为客户创建一个新的目标。

现在我需要开发 Package 类,我必须在其中维护对已运送包裹的目的地的引用。按照 DDD 规则,我不能直接引用子实体 id,因为我不能引用另一个聚合的子实体。

我需要做什么?

  1. 我是否必须使用目标所需的数据创建一个新的 ValueObejct 并公开它?(通过这种方式,我能够保留只能通过 Customer AggregateRoot 以书面形式访问的 Destionation 的封装和规则)。通过这种方式,外部类可以访问目标的字段并使用这些字段执行逻辑/检查,但不能更改目标的状态。
  2. 我是否必须添加一个 DestinationNumber 字段(在客户中渐进式),创建一个包含 CustomerId 和 DestinationNumber 的 ValueObject 并在 Package 聚合中使用它?
  3. ?

有人可以帮帮我吗?请详细说明回复,因为我想了解更多这种情况。

4

2 回答 2

1

如果我理解正确,我会假设这个Package 必须交付到某个地方,到Destination

我还假设客户确实是一个实体,也是一个聚合根,因为它会具有更改名称等行为。也是一个实体,也是它自身的聚合根,因为它与客户具有不同的事务边界和也有一些行为(如addDestination)。

在这里,我不认为Destination应该是一个实体,因为它本身没有任何行为或身份,而只是包含这个Destination引用Customer的事实。因此,我会将Destination建模为一个值对象,并具有 CustomerId 的属性(可以这样做,因为Customer是一个聚合根)。

addDestination的参数将是Destination的值对象。如果Package必须更改目的地,它可以创建另一个Destination实例并丢弃旧的实例。

此外,如果DestinationCustomer的子级,也没有任何意义,因为可能有一些Customer还没有交付任何东西。相反,我会将Destination建模为Package的子级,因为有意义的是,每次创建Package时,它​​都应该有一个Destination(假设必须交付任何Package )。所以PackageDestination是同一个集合。

于 2020-12-11T03:39:59.827 回答
0

DDD 中的聚合更多的是关于事务边界,而不是表示一个实体的存在在没有另一个实体的情况下是否有意义。


假设 A -目的地应该是一个聚合

如果在您的域中在其自己的事务中修改Destination而不必考虑客户是有意义的,那么您可能错过了 Destination 作为它自己的聚合。

如果目的地将在客户之间共享或从一个客户移动到另一个客户(当然是通过身份参考),这也是有意义的。


假设 B - Destination绝对是Customer子实体

另一方面,如果只有客户聚合可以确保始终满足客户目的地的业务不变量,则目的地应仅在客户的事务边界内更改,从而保持客户的子实体。

例如,在您的Package聚合中,您当然只能记住 Destinations id,如果您只需要它用于阅读目的。

但认为这不是一个好主意。另一方面,在参考中不包括客户的 id 会使这种关系不那么明确。另一方面,如果 Destination 确实是一个实体(而不仅仅是一个值对象),则 Destination 的 id 只需要在相应客户边界内的域中是唯一的。

因此,如果 Destination 真的是 Customer 的一个子实体,我会选择您的选项 2,方法是引入一个参考值对象,例如CustomerDestination ,包括客户 ID 和任何适合在特定客户范围内识别目的地的标识符。

注意:这不会影响您的数据库模型,根据您的设计方式,您可能仍然需要数据库中的一些全局唯一代理 ID。

于 2020-12-10T16:11:18.057 回答