在领域驱动设计中,为什么要使用“聚合”对象以及在设计聚合时要应用哪些关键原则?
In domain driven design, why would you use an “aggregate” object and what key principles would you apply when designing an aggregate?
我是 DDD 的新手,所以我只是了解它的基本概念。有人可以指导我更多地了解 DDD 中的聚合对象吗?特别是,为什么要使用“聚合”对象以及在设计聚合时要应用哪些关键原则?
谢谢,
聚合是一个一致性边界
这个概念允许您指定哪些实体可以自动更改,哪些不可以。
这也使聚合成为通过存储库加载和持久保存数据的主要容器,即存储库处理整个聚合,而不是单个实体。
要找到合适的聚合设计,您需要充分了解您的用例并尝试找出哪些实体需要在一个事务中以原子方式更改,以及您可以在哪些地方求助于最终一致性。尝试使聚合变小,但始终首先考虑用例。
让我们从头开始吧。很久以前,在一个遥远的星系中,有 SQL 个数据库 ACID transactions。我们在这里真正感兴趣的是 ACID 首字母缩略词的原子性和一致性。例如,如果您有两个更改 a1 -> a2
和 b1 -> b2
并且您在这样的事务中进行了更改,那么更改必须是原子的并且您将只有 2 个有效的状态选项:a1, b1
和 a2, b2
。因此,如果 a1 -> a2
更改失败,则整个事务失败。这叫做即时一致性。
相比之下,没有SQL 数据库不符合 ACID。通过这些数据库,您的更改不是原子的,您可以有多个状态:a1, b1
、a2, b1
、a1, b2
、a2, b2
,具体取决于更改的顺序或哪些更改失败。这称为最终一致性。
如果您的分布式系统具有涉及多台计算机的复杂更改,那么您有两种选择。可以使用最终一致性,速度会很快,但是数据在多台机器上不会一致。或者可以用立即一致性2 phase commit,更新会很慢,但是机器之间数据会保持一致。
通过DDD,聚合是一个一致性边界:内部变化具有即时一致性,外部变化具有最终一致性。继续使用同一个例子,你想用你的命令改变两件事:a1 -> a2
和 b1 -> b2
。如果这些东西在同一个聚合 x
中,那么您可以在同一个事务中更改它们:(x.a1, x.b1) -> (x.a2, x.b2)
使用即时一致性。如果这些东西在不同的聚合中:x
、y
,那么你不能在同一个事务中更改它们,所以你必须使用最终一致性:x.a1 -> x.a2
和y.b1 -> y.b2
将是将彼此独立提交的两个事务。
DDD 有一条根据 Vernon 的书的规则;您不能在单个事务中更改多个聚合。如果你这样做,那么它就是一种代码味道,这是一个标志,以不同的方式选择一致性边界,或者换句话说,以不同的方式设计聚合。
因此,通过设计聚合,您必须牢记这些一致性边界。如果你不这样做,那么它会导致并发问题。例如,您的聚合 x
上有 a
和 b
属性。这些属性彼此独立,因此 (x.a1, x.b2)
和 (x.a2, x.b1)
都是有效状态。如果约翰想在两个并发请求中更改 x.a1 -> x.a2
而简想更改 x.b1 -> x.b2
,那么其中一个请求将失败,尽管这两种情况都是:(x.a1, x.b1) -> (x.a2, x.b1) -> (x.a2, x.b2)
和 (x.a1, x.b1) -> (x.a1, x.b2) -> (x.a2, x.b2)
会产生相同的状态 (x.a2, x.b2)
并且每个步骤都是有效的。因此,如果同时处理同一聚合,John 或 Jane 都会有糟糕的一天。如果他们发送多个并发请求,则可能是他们两个。您可以通过创建新聚合 y
并将 b
属性 移动到其中来解决此问题。所以变化会是x.a1 -> x.a2
和y.b1 -> y.b2
两笔交易,不会造成麻烦。
如果您有两个聚合 x
和 y
并且属性 x.a
和 y.b
不能独立更改,则为反例。所以 x.a1, y.b2
和 x.a2, y.b1
状态是无效的。这是将这两个聚合合并为一个并使用立即一致性而不是最终一致性的标志。
您的系统很可能会 运行 在多台机器上运行。较大的组件,如限界上下文、聚合将最终保持一致,而较小的组件,如值对象、实体将立即保持一致。因此,您可以在多台机器上部署您的限界上下文,而无需分布式事务和两阶段提交,这将产生一个快速可靠的系统。另一方面,由于您使用的交易,聚合只能具有有效状态。
请注意,我不是该主题的专家,我只是看了一本书。 =]
1 年后:
我找到了一个很good article about aggregates。根据它,您应该在不变量周围设置一致性边界以防止违反合同。所以如果你有一个不变量列表,那么你可以使用它们来定义一致性边界。聚合边界将是相似的。理想情况下它们包括所有不变量,但如果它们变得太大,它们将导致太多并发异常,因此在复杂的情况下它们不能包括一些不变量。
我是 DDD 的新手,所以我只是了解它的基本概念。有人可以指导我更多地了解 DDD 中的聚合对象吗?特别是,为什么要使用“聚合”对象以及在设计聚合时要应用哪些关键原则?
谢谢,
聚合是一个一致性边界
这个概念允许您指定哪些实体可以自动更改,哪些不可以。
这也使聚合成为通过存储库加载和持久保存数据的主要容器,即存储库处理整个聚合,而不是单个实体。
要找到合适的聚合设计,您需要充分了解您的用例并尝试找出哪些实体需要在一个事务中以原子方式更改,以及您可以在哪些地方求助于最终一致性。尝试使聚合变小,但始终首先考虑用例。
让我们从头开始吧。很久以前,在一个遥远的星系中,有 SQL 个数据库 ACID transactions。我们在这里真正感兴趣的是 ACID 首字母缩略词的原子性和一致性。例如,如果您有两个更改 a1 -> a2
和 b1 -> b2
并且您在这样的事务中进行了更改,那么更改必须是原子的并且您将只有 2 个有效的状态选项:a1, b1
和 a2, b2
。因此,如果 a1 -> a2
更改失败,则整个事务失败。这叫做即时一致性。
相比之下,没有SQL 数据库不符合 ACID。通过这些数据库,您的更改不是原子的,您可以有多个状态:a1, b1
、a2, b1
、a1, b2
、a2, b2
,具体取决于更改的顺序或哪些更改失败。这称为最终一致性。
如果您的分布式系统具有涉及多台计算机的复杂更改,那么您有两种选择。可以使用最终一致性,速度会很快,但是数据在多台机器上不会一致。或者可以用立即一致性2 phase commit,更新会很慢,但是机器之间数据会保持一致。
通过DDD,聚合是一个一致性边界:内部变化具有即时一致性,外部变化具有最终一致性。继续使用同一个例子,你想用你的命令改变两件事:a1 -> a2
和 b1 -> b2
。如果这些东西在同一个聚合 x
中,那么您可以在同一个事务中更改它们:(x.a1, x.b1) -> (x.a2, x.b2)
使用即时一致性。如果这些东西在不同的聚合中:x
、y
,那么你不能在同一个事务中更改它们,所以你必须使用最终一致性:x.a1 -> x.a2
和y.b1 -> y.b2
将是将彼此独立提交的两个事务。
DDD 有一条根据 Vernon 的书的规则;您不能在单个事务中更改多个聚合。如果你这样做,那么它就是一种代码味道,这是一个标志,以不同的方式选择一致性边界,或者换句话说,以不同的方式设计聚合。
因此,通过设计聚合,您必须牢记这些一致性边界。如果你不这样做,那么它会导致并发问题。例如,您的聚合 x
上有 a
和 b
属性。这些属性彼此独立,因此 (x.a1, x.b2)
和 (x.a2, x.b1)
都是有效状态。如果约翰想在两个并发请求中更改 x.a1 -> x.a2
而简想更改 x.b1 -> x.b2
,那么其中一个请求将失败,尽管这两种情况都是:(x.a1, x.b1) -> (x.a2, x.b1) -> (x.a2, x.b2)
和 (x.a1, x.b1) -> (x.a1, x.b2) -> (x.a2, x.b2)
会产生相同的状态 (x.a2, x.b2)
并且每个步骤都是有效的。因此,如果同时处理同一聚合,John 或 Jane 都会有糟糕的一天。如果他们发送多个并发请求,则可能是他们两个。您可以通过创建新聚合 y
并将 b
属性 移动到其中来解决此问题。所以变化会是x.a1 -> x.a2
和y.b1 -> y.b2
两笔交易,不会造成麻烦。
如果您有两个聚合 x
和 y
并且属性 x.a
和 y.b
不能独立更改,则为反例。所以 x.a1, y.b2
和 x.a2, y.b1
状态是无效的。这是将这两个聚合合并为一个并使用立即一致性而不是最终一致性的标志。
您的系统很可能会 运行 在多台机器上运行。较大的组件,如限界上下文、聚合将最终保持一致,而较小的组件,如值对象、实体将立即保持一致。因此,您可以在多台机器上部署您的限界上下文,而无需分布式事务和两阶段提交,这将产生一个快速可靠的系统。另一方面,由于您使用的交易,聚合只能具有有效状态。
请注意,我不是该主题的专家,我只是看了一本书。 =]
1 年后:
我找到了一个很good article about aggregates。根据它,您应该在不变量周围设置一致性边界以防止违反合同。所以如果你有一个不变量列表,那么你可以使用它们来定义一致性边界。聚合边界将是相似的。理想情况下它们包括所有不变量,但如果它们变得太大,它们将导致太多并发异常,因此在复杂的情况下它们不能包括一些不变量。