DDD集合:在另一个集合中持有非根实体标识符的实体

Ste*_*ven 2 entity domain-driven-design aggregate

我正在尝试了解实体和集合之间的关系的最佳实践。

设想一个设计,其中有一个由两个实体组成的Product Aggregate

总根:产品

子实体:Sku

那里的产品可以有很多Skus。Skus和产品的零件号和名称是不变的,因为更改其中一个的名称必须以事务方式确保另一个已更新。同样,产品集合需要确保绝不存在重复的Skus。

我们还有另一个聚合:StorageLocation。存储1个或更多Skus的位置。但是,重要的是,StorageLocation知道要存储的特定Sku。即。在加拿大的StorageLocation应该存储该国本地的Sku,而不是用于美国市场的Sku。

在我看来,这意味着StorageLocation需要保留对Sku的引用(因为对产品汇总根的引用本身并未提供足够的信息来确定要存储的Sku)。

根据我的阅读,这似乎打破了另一个聚合对象不应持有对另一个聚合对象中非根实体的引用的原则。所以问题:

  • 在存储位置汇总中仅保留产品和Sku的标识符是可以接受的吗?
  • 我知道瞬态引用被认为是允许的,但在这种情况下(至少据我所知),此信息需要存储。如上所述,仅存储对Product(或ProductId)的引用是不够的。
  • 产品和Skus具有自然标识符(零件号,Sku号)。这是否为将这些值存储在StorageLocation聚合中提供了更大的灵活性,因为它们的含义超出了技术含义。
  • 我是用错误的方式来处理这个问题,需要以不同的方式看待事情。我经常发现很难突破PK / FK的心态。

谢谢

Chr*_*mon 7

不存储对子实体的引用的指导原则是建立在良好的原则基础上的,但我相信通常会引起混乱。目标实际上是子实体不一定具有“全局唯一”标识符,并且没有存储库使用此类全局唯一标识符直接访问子实体。

但是,如果您StorageLocation拥有的全局唯一标识符Product以及SKU的可能是本地唯一标识符,那么以下模式就没有问题:

var storageLocation = _storageLocationRepository.Get(id);
var product = _productRepository.Get(storageLocation.ProductId);
product.DoSomethingToSku(storageLocation.SkuId);
Run Code Online (Sandbox Code Playgroud)

关键是,通过确保始终从存储库中获取产品,然后通过该产品与子实体进行交互,可以确保该产品有机会保护其自身的不变量。

总结一下:

  • 随意将标识符存储到子实体,只要您还存储其聚合根的全局标识符即可。
    • 在这种情况下,子实体ID可以是本地唯一的,也可以是全局唯一的-没关系,只要通过聚合根访问子实体即可。
  • 永远不要直接从存储库中加载子实体并在其上调用方法-否则聚合根就没有机会保护其不变式。
    • 实际上,您甚至永远都不应拥有子实体的存储库


Aar*_*ins 5

仅在 StorageLocation 聚合中保留产品和 Sku 的标识符是否可以接受?

是的,因为标识符是 DDD 中的值对象,所以将标识符存储在 StorageLocation 聚合中不会违反保存对另一个聚合的子实体的引用的规则,因为值对象只是一个值对象,不再与原始实体有任何直接关联总计的。

我知道瞬态引用被认为是允许的,但在这种情况下(至少据我所知)需要存储此信息。如前所述,存储对 Product(或 ProductId)的引用是不够的。

StorageLocation 聚合根脱水回数据库应包括需要持久化的所有内容。基础设施层决定了域对象如何存储在物理存储库中,并且可能是与域模型完全不同的模型设计,具体取决于持久性技术的问题。

产品和 SKU 具有自然标识符(零件号、Sku 号)。这是否为将这些值存储在 StorageLocation 聚合中提供了更大的灵活性,因为它们的含义超出了技术含义。

没有什么可以与“更大的灵活性”进行比较,因为您只会使用自然标识符或瞬态引用来引用 StorageLocation 聚合中的 Product 聚合内的实体。

我是否以错误的方式处理这个问题,需要以不同的方式看待事物。我经常发现很难摆脱PK/FK的心态。

请记住,持久层问题不应与域模型纠缠在一起,其目的是使域模型清晰并与当前业务规则同义。