【问题标题】:Many-to-many relationships in DDDDDD 中的多对多关系
【发布时间】:2026-01-08 05:05:01
【问题描述】:

我是 DDD 的新手,我被多对多关系困扰。例如。我们有两个聚合根 - Tasks 和 Workers。

Contract 绝对不是聚合根,因为没有 Task 和 Worker 就没有意义。所以,它应该是一些聚合的一部分。但它应该属于哪个聚合?我们需要知道所有任务合同的汇总成本和所有工人合同的汇总成本。在 Task 和 Worker 中收集合同对我来说是很自然的。

好吧,我可以将成本计算转移到域服务中,但我担心这是向贫血模型迈出的一步。是否有处理多对多关系和保留到达域模型的通用方法?

谢谢!

【问题讨论】:

  • 一个切题的问题 - 你用什么来创建这个图表?

标签: c# domain-driven-design


【解决方案1】:

通过关注侧边栏中链接的相关问题,我发现了这篇有趣的文章:

DDD & Many to Many Object Relational Mapping

它似乎推荐了我直觉上的想法:事实上,worker 和 task 依赖于合同是不自然的。也就是说,“工人”的概念在没有“合同”概念的情况下仍然有意义(对于任务也是如此),因此体现该概念的实体不应依赖于合同实体。

要显示分配给给定任务的合同或分配给给定工作人员的合同,您需要运行域查询。这实际上是对域服务的适当使用,如果您考虑一下,它可以更好地反映您的域的实际情况。

我还注意到您说“Contract 绝对不是聚合根,因为没有 Task 和 Worker,它没有任何意义。”这实际上是 Contract 聚合根的确切原因。

因此,我的建议是,结合 cmets 中 arootbeer 的见解:

【讨论】:

  • 我认为将GetCosts() 放在TaskWorker 上会很混乱——TaskWorker 都没有体现明确的成本;这明确地是Contract 的域。一个Worker 有一个Rate,一个Task 有一个PeriodDurationContract 中的两者的组合指定了 CostGetCosts() 可能是域搜索聚合函数。
  • @arootbeer:可能是对的!我不太了解域的成本部分,所以我只是将它们复制过来。我将对其进行编辑以反映您的见解。
  • 好吧,我也在考虑将 Contract 设置为 Aggregate root。但真正的商业价值不是合同成本 - 它是任务和工人的总合同成本。因此,当 Task 和 Worker 都没有 Contracts 集合时,模型变得贫乏。我们需要按服务加载所有合同并计算成本总和。顺便说一句,工人没有费率 - 不同的合同有不同的成本。
【解决方案2】:

Contract 在我看来是您设计中的一流对象。您声称它在 workertask 的上下文之外没有意义当然是正确的,但这并不意味着它本身不是聚合根。

大概Contract 有自己的逻辑来计算它的成本,基于与之关联的taskworker 的一些属性。同样,TaskWorker 包含的上下文与 Contract 无关。

您需要跨越的差距是将相关上下文移动到Contract对象中。让它存储worker的费率和task的周期(除了各自的ID,上面只隐式建模),并动态计算成本。

--编辑--

正如 Domenic 所说,您的评论非常适合作为后续问题的候选者。但我会说,一旦你将TaskWorker ID 放到Contract 上,报告就变成了一件小事。

【讨论】:

  • 这只是一个例子。因此,我们可以将 Contract 视为没有成本计算逻辑的实体 - 为工作支付的简单金额。有没有办法为总任务/工人成本计算建立覆盖模型?
  • @lazyberezovsky 这是一个很好的问题——不过肯定会问它作为后续问题,因为它有点偏离了方向。当您澄清商业价值以这种方式发挥作用时,它确实让我重新思考事情,我不确定我会做什么......
  • 不会有点贫血吗?没有逻辑,只有数据和服务中的所有代码。需要考虑这个..
  • @lazyberezovsky - 它至少具有将术语乘以速率的基本逻辑,但这仍然是逻辑。它使您能够在不改变依赖关系的情况下在未来创建更复杂的合约。
最近更新 更多