首页 > 解决方案 > 如何处理有很多孩子的大聚合根?

问题描述

我对 DDD 很陌生,在我们最近的项目中,我们遇到了一个我没想到的问题。在我们的领域中,我们模拟公司的预算。

为简单起见,预算是一个包含一堆行和列的表格。公司的每个部门在特定年份都有不同的预算。该表可能有相当多的行和固定数量的列(基本上,每个月的名称和值)。

我们的业务人员想要执行的业务规则适用于整个表级别,例如您不能有两个同名的行或不能编辑锁定的表等。因此,经过一些详细说明,我们决定制作一个在这个边界上下文中表一个聚合。

然后事情开始变得有趣。

基本上,我们现在有两个问题:

  1. 一个表格可以被多个用户同时编辑,即使有人在编辑一个单元格而另一个人编辑完全不同的单元格,从聚合的角度来看,这些操作是对单个聚合的并行编辑,并且每一个它们要求我们加载整个聚合、应用更改、检查业务规则并将其保存回数据库。
  2. 由于我们使用事件溯源,load每次提交到数据库的事件都会变得越来越慢,因此我们决定使用快照方法。我们每 X 个事件进行一次快照,因此加载操作不会花费很多时间。但在某个时候,我们意识到一周后我们的一张表有数千次编辑,并且快照事件是一个巨大的 json 字符串,如 1_000_000 个字符长。即使从数据库中传输它也很慢。

此时我开始认为将整个表作为一个聚合是一个错误,我们可以采取更细化的方法,但我不知道我可以参考 DDD 中的任何此类规则,也不太明白如何如果我只是将聚合拆分为行或类似的东西,我可以在整个表上强制执行业务规则。

谁能告诉我我哪里错了,我可以做些什么来改进模型,并参考我可以与团队推理的来源?

标签: concurrencylanguage-agnosticdomain-driven-designevent-sourcingdomain-model

解决方案


聚合是一致性边界,这通常意味着您出于遇到的原因希望它们保持较小。

将表作为一个聚合表可能是有意义的,以便可以强制/处理表级约束和转换(例如锁定/解锁状态,当然)。但我不确定表是否需要包含行的所有内容:每行的内容可以建模为它自己的聚合,表聚合跟踪行的顺序(显然,访问行将通过聚合根)。

在这种方法中,表聚合可以在只涉及表的写入时强制执行不变量,而行聚合可以在只涉及单个行的写入时强制执行不变量。必须通过至少一个投影来执行跨多行的不变量,这意味着系统不能保证拒绝所有违反不变量的写入。

这意味着几乎肯定最终会出现期望的不变量被违反的情况(在这种情况下,将其称为不变量有点滥用术语,但请耐心等待......),以及系统(包括用户、操作员等)将需要采取补偿措施来恢复不变量。补偿动作的本质是一个商业问题:有时它可以自动化,有时它是手动操作的警报问题。

如果这实际上是不可接受的,并且业务需求使得表需要能够强制执行覆盖多行内容的不变量,那么您将完全被巨大的表聚合所困扰(请注意,即使您遇到了这个问题你没有做 DDD)。

根据您使用哪种语言实现,您可能会发现利用 Actor 模型并让每个表成为单个 Actor 的内部状态在性能方面是有回报的:Actor 始终有效地充当内存中的角色——聚合的当前快照(因为 actor 一次只会处理一条消息)(自上次持久化快照以来的事件以及此后的事件只需要重播一次以重新水化 actor)。如果表状态是一百万字节的序列化 JSON,数据大小似乎不会迫使您利用某些参与者框架的集群功能,因此任何参与者模型实现都应该有效。 本演讲探讨了这种方法的各个方面


推荐阅读