领域驱动设计建议

Domain Driven Design Advice

我有一个包含以下内容的域:

一个场地可以有多个建筑,一个建筑可以有多个房间。

我的第一个想法是将 Venue 作为 AggregateRoot,而 Building 和 Rooms 是 Entities。 建筑物也有一个地址,它是一个 ValueObject。

然后我的第二个想法是我需要在不通过场地的情况下更新建筑物或房间。在那种情况下,让 Venue, Building & Room 制作 AggreagateRoots 不是更可取吗?还是我应该保持这样?如果是这样,我该如何更新建筑和房间?

如果您将您的概念建模为一个集合,则您不能再引用一个房间或一座建筑物。这些被认为是场地的内部状态。例如,您应该实施 VenueRepository,但不实施 BuildingRepositoryRoomRepository。修改房间必须被视为修改场地的一部分。因此,您必须从存储库中检索场地,然后导航到房间进行更改,然后保存场地的新状态。此外,外部环境不得参考房间或建筑物,而只能参考整个场地。这需要一些非常具体的用例才能理解。

您的建筑物有地址这一事实很好地暗示了它不可能是聚合体中的 non-root 实体。你完全可以想象让你的建筑物留在原地,同时摧毁你的场地以建造另一个,在此过程中改变建筑物的地址。房间更棘手。没有建筑物,房间就不可能存在,因此两者之间有着紧密的联系。总的来说,您是应该将房间作为 building-room 集合的一部分还是作为一个独立的实体,取决于您要建模的内容以及您要在系统中实施的业务约束。

假设您有一条业务规则,其中规定:如果建筑物有超过 5 个房间且每个房间的任何设备价值超过 10 万欧元,您需要一位主管批准关闭建筑物的电源。您需要在房间级别对您的建筑物进行建模,但您实际上并不需要 random-access 每个单独的模型。这是因为您只是在建筑物级别执行规则。在那种情况下,您可以使用聚合。相反,如果您正在构建一个工具来帮助人们在房间里安排会议,那么您需要将房间作为会议的实体来引用它们。

决定一个实体是另一个聚合下的实体还是聚合根本身取决于您域的用例。

一般来说,我使用以下经验法则来确定一个实体是否应该毕业成为一个聚合体:

  • 实体有复杂的业务规则和自己的生命周期

    具有复杂业务逻辑和自身生命周期的实体最好保持独立以管理复杂性。

    假设您域中的一个房间有许多状态、相关规则和权限结构(经理可以对房间执行操作,但前台员工不能)。在那种情况下,它最好作为聚合。

  • 需要直接访问和操作实体

    当您觉得除了访问实体之外没有其他原因加载聚合时,将它们作为独立元素可能会更好。

  • 实体及其聚合不需要共享事务边界

    如果没有将实体绑定到聚合的业务规则,将其升级为聚合根会更容易。

    相反,当聚合与其实体之间存在不变量,并且这些不变量不能最终一致时,您需要将实体封装在聚合下,并仅通过聚合操作实体。

  • 有时,实体可以独立存在而不依赖

    例如,您想管理建筑物而不用担心场地,因为您 sub-leasing 是场地的一部分。

  • 将实体包含在聚合下会导致性能问题

    请记住,聚合不是 lazy-loaded;它们被完整加载以保证不变量保持满足。如果您的建筑物有超过 1000 个房间,则每次加载建筑物聚合时都会使用更多的内存。

相反,请记住,您不能再通过将实体分解为聚合(强约束)来满足不变量。您将不得不满足于最终一致性并管理工作流,在这些工作流中您可能会发现问题,同时使域最终保持一致。