Domain driven design DDD-如何在实体必须引用非根实体的情况下形成聚合

Domain driven design DDD-如何在实体必须引用非根实体的情况下形成聚合,domain-driven-design,Domain Driven Design,我有一些实体,我试图遵循领域驱动的设计实践来识别聚合。我无法做到这一点,因为我打破了实体不能引用其他聚合的非根实体的规则,或者我根本无法形成聚合 我有以下实体:组织、工作机会、候选人和工作申请 一个组织创造工作机会,但可能只有有限数量的有效工作机会 应聘者创建工作申请,但可能只有有限数量的活动工作申请 JobApplication引用了它所针对的JobOffer 基于此,我必须知道一个组织有多少份工作机会,然后才能创建一份新的工作机会(强制执行限制),我假设该组织应该是拥有工作机会的根实体。

我有一些实体,我试图遵循领域驱动的设计实践来识别聚合。我无法做到这一点,因为我打破了实体不能引用其他聚合的非根实体的规则,或者我根本无法形成聚合

我有以下实体:组织、工作机会、候选人和工作申请

  • 一个组织创造工作机会,但可能只有有限数量的有效工作机会
  • 应聘者创建工作申请,但可能只有有限数量的活动工作申请
  • JobApplication引用了它所针对的JobOffer
基于此,我必须知道一个组织有多少份工作机会,然后才能创建一份新的工作机会(强制执行限制),我假设该组织应该是拥有工作机会的根实体。这同样适用于候选人和工作申请。现在我有两个集合:有工作邀请的组织和有工作申请的候选人。但是我需要参考工作申请中的工作机会。。。这打破了我不能引用非根实体的规则


我在这个论坛上寻找并发现了类似的问题,但不知何故我仍然无法找到答案,因此提前表示歉意-我感谢您的帮助。

I general,您应该避免保留对其他聚合的对象引用,而是通过id引用其他聚合。在某些情况下,在另一个聚合中引用其中的某个实体是有效的,但这也应该通过id来完成

如果您这样做,您应该引用一个复合id。聚合旨在描述逻辑边界和事务边界。作为聚合的一部分建模的子实体ID只需要在该聚合的边界内是唯一的。这使得在系统中执行操作时更容易将注意力集中在这些边界内的内容上。即使您正在使用UUID(或GUID),如果您确实需要引用另一个聚合的子实体(比如说您有充分的理由),您也应该通过聚合根对id图进行建模,这意味着始终知道另一个聚合的id以及您感兴趣的实体的id。这意味着引用一个复合id

但是:当我认为需要引用另一个聚合根的子实体时,我首先会更深入地研究这个问题。这意味着这个子实体作为一个独立实体可能也很重要

我没有发现另一个聚合根吗?

在您的情况下,查看您的域模型图,我怀疑JobOffer本身应该是一个聚合。当然,我不知道你的域名,但我至少可以猜测,在你的系统中可能有一些交易允许自己改变工作机会,而不需要考虑特定于组织的业务不变量。如果是这样的话,你应该重新考虑域模型,并考虑将<强> JobOffer聚合为自己的< /强>根。在这种情况下,您的初始问题会自动得到解决。还请注意,将工作机会建模为聚合可以简化对组织执行的操作,并且在加载组织聚合时不需要加载该组织的所有工作机会。这当然可能与您的情况无关,实际上取决于一个组织的最大招聘数量

因此,我认为,根据您的业务需求和域逻辑不变量,我将重新编译以下两个选项之一:

  • 仅通过复合id引用外部子实体,该复合id包括聚合的其他子实体id+子实体id(例如,通过创建将此引用表示为强类型的某个值对象)
  • 如果上述考虑因素在您的情况下成立,请自行将JobOffer汇总

非常感谢您的详细回复:这很有道理!我想知道这份工作的总体情况。它不是必须属于以组织为根的组织吗因为创造新的工作机会需要检查组织已经有多少工作机会?如果JobOffer是它自己的聚合,那么Id必须实现一个创建JobOffers的服务,该服务在允许创建新的JobOffers之前,首先向存储库请求该组织拥有的JobOffers总数。-是这样吗?同样,这也适用于候选人和职位申请,因为如果职位申请可以是自己的聚合,那么没有理由职位申请也不应该是自己的聚合?组织聚合仍然可以保留职位申请参考(即职位申请ID)的列表,例如通过值对象。然后组织可以提供一个工厂方法来创建一个新的工作机会实例。此方法还可以确保未超过工作邀请的最大数量,否则返回错误。其他的突变/变化发生在工作提供可能有自己的交易边界,而不需要考虑组织。但这同样取决于您的域逻辑细节。这同样适用于工作应用程序。您可以看看沃恩·弗农(Vaughn Vernon)对敏捷项目管理应用程序的实现。有一个backlog项本身就是一个聚合项,尽管您可能会说它属于一个特定的项目。看见