设计具有大量子项的聚合根

时间:2018-03-12 08:30:43

标签: c# entity-framework domain-driven-design

我开始了一个新项目(重写现有解决方案),并希望使用和探索DDD。在设计我的域模型时,很难在开始时忽略实体框架。它适用于只有小型子集或只有CRUD的小型集合。现在我处在一个点,我有一个大集合的集合,我为此疯狂。

域概述

  • 在我的域模型中,有一个名为协议(聚合根)。
  • 协议的子项称为协议条目
  • 协议条目具有特定类型(默认每月每年)。至于现在,除了知道它们是什么类型之外没有其他要求 - 它们都是相同的,行为相同但可能有不同的类型(请继续阅读以找出原因)。
  • 现在,在这个月中,为特定的协议添加了几个协议条目(类型:默认)。无法设置或定义协议条目的日期(时间) - 使用"现在"添加/创建它。
  • 每个月末(12月除外,请参见下一个),要求创建/添加协议条目(类型:每月) ,在再次添加任何其他协议条目之前。
  • 每年年底,要求在任何其他协议之前创建/添加协议条目(类型:年份)可以再次添加条目。在年底,每月条目不是必需的,因为已经有了年度。

这可能看起来很恶心,但最后可能看起来像一个非常简单的问题需要解决。它是/除了以下我想要实现的东西。我不知道并且事件无法找到任何相关的东西给我答案,如果我再次考虑持久性/性能,当我不应该在域设计期间那么多。或者我的方式无法解决。

其他信息

由于每月每年 协议条目是从系统本身(后台作业/服务)创建的,因此用户赢得了&#39;事件关心这一点。在00:00新月/年开始时添加协议条目时可能会出现短暂且短暂的错误,因为后台作业/服务尚未完成每月<当前协议的/ em>或年度条目 - 确定无误!

旧解决方案中的协议条目有两个名为 NextMonthlyEntry NextYearlyEntry (DataType:DateTime)的字段。有了这些,就可以轻松而自然地询问聚合问题,例如&#34; protocol.IsYearlyOverdue()&#34;并且它还有助于在后台作业/服务中查询所有过期&#34;过期&#34;并处理每月每年条目。我在新解决方案中重用了这些字段。

因为协议在一个月内可以拥有数千个协议条目,而在一年中甚至更多,所以每次我都无法在聚合根中加载所有这些只要协议对这些内容没有要求(就像客户AR一样不能如果不要求客户不允许一次只有5个订单,那么一直有所有订单。

问题

如何在每个协议中加载所有条目并保持行为,例如检查是否允许将条目添加到协议(我确信它与协议相关),因为它必须更新协议的(行)版本以及添加条目时的Next(Monthly | Yearly)Entry字段,以确保一致性并了解并发问题。

当我的协议获取名为 AddedEntries 的私有字段时,该字段会填充新条目。由于条目永远不会被编辑/删除,因此只能跟踪这些条目。所以我可以添加一个或多个条目,并确保一致性和验证规则。在Domain和DDD方面对我来说似乎很好,但现在在持久性方面,如何将这些 new 条目添加到数据库中?我不能只使用集合更新协议,因为这将删除不在集合(EF)中的现有条目。如果有任何条目,是否需要存储库(ProtocolRepository.Update(协议))向依赖表添加条目?

很抱歉这个问题很长,可能不是完美的标题(不知道如何用几句话来概括)。我是DDD的新手,这个问题让我发疯,如果不是一切都完全正确我也很抱歉 - 我还在学习。提前感谢大家的时间关心和思考我的问题!

2 个答案:

答案 0 :(得分:1)

如果你想和很多孩子一起工作,请查看Vaughn Vernon的作品,特别是他在Calendar和CalendarEntry上的工作。它在Java中,但看起来非常类似于你:

https://github.com/VaughnVernon/IDDD_Samples/tree/master/iddd_collaboration/src/main/java/com/saasovation/collaboration/domain/model/calendar

注意如何建立关系,Calendar从不拥有任何CalendarEntry,而CalendarEntry引用CalendarId。

https://github.com/VaughnVernon/IDDD_Samples/blob/master/iddd_collaboration/src/main/java/com/saasovation/collaboration/domain/model/calendar/Calendar.java

https://github.com/VaughnVernon/IDDD_Samples/blob/master/iddd_collaboration/src/main/java/com/saasovation/collaboration/domain/model/calendar/CalendarEntry.java

当您处理Calendar或CalendarEntry时,应用程序服务负责加载您要处理的集合,使对象进行交互并持久化它们。这样,您可以更好地控制粒度,如果您只需要当前月份的条目子集,则无需加载完整聚合,从而提高性能和内存占用。

记住DDD就是量身定制的。

因此,在您的存储库中,您可能拥有以下方法:

  • (负载)entriesForCurrentMonth
  • (负载)entriesForNextMonth

与他们合作,然后保存他们。

答案 1 :(得分:1)

在进行实际实施之前,您需要考虑不变量并相应地塑造模型。

  • 确定在添加var a = ["foo", "bar"]; var b = a.slice(); // array copy console.log(`a is ${a}`); console.log(`b is ${b}`); b.splice(1, 1); console.log(`a is ${a} (not changed)`); console.log(`b is ${b}`); b = a;时强制执行不变量所需的数据,并在无处不在的语言中找到有意义的名称(是Entry吗?)

  • 您是否可以在没有性能问题的情况下将该内容作为Aggregate的一部分加载?

    • 是:将其作为聚合中的实体。
    • 否:如果您可以使用最终一致性来强制执行规则并将其设置为单独的聚合,请与您的域专家联系。

这里的问题可能是过度概括 - 将所有内容视为另一个MonthlyProtocolEntry会阻止您考虑所有可能的设计,尤其是那些具有更专业,细粒度部分的设计。