DDD聚合和值对象

时间:2011-09-19 21:45:43

标签: domain-driven-design

我想问一下有关DDD功能的问题。假设我们有两个聚合,每个聚合都包含值对象地址。因此,对于Eric Evans DDD,我们应该将聚合彼此隔离,因此第一个聚合的聚合根不能具有到Address的链接。坦率地说,这对我来说似乎没有意义,所以问题是如何解决这种情况?哪个聚合应该包含地址?

由于

3 个答案:

答案 0 :(得分:14)

您可以使用相同的值对象。但只有当聚合根存在于相同的有界上下文中时才这样做,因此对于两个聚合具有相同的含义。如果聚合存在于不同的有界上下文中,则有2个单独的并且重复。将一个有限的背景问题泄漏到另一个环境中的是Eric正在努力解决的问题。

对大多数人而言,实体与价值对象的关注归结为人们在重复数据方面存在问题。我们受过如此训练,无法以单一规范模型的第三范式进行思考。 DDD通过在需要的地方强制复制而带来不可避免的复杂性,并允许曾经被认为是一对多的概念。

希望这有帮助

答案 1 :(得分:8)

  

值对象是描述某些特征或对象的对象   属性但不带有身份的概念。

由于它没有概念身份,因此您无法“引用”或“拥有链接”。你只能'包含'它。假设您有用户,用户有年龄。年龄是一个价值对象。如果约翰25岁,简也25岁,他们就不会“引用”相同的年龄。 Jonh的年龄与Jane的年龄完全相同。因此,如果您的地址确实是一个价值对象,那么您不会违反任何聚合边界。您的聚合根只有相同的地址。即使你在技术上有java / c#引用它也没关系,因为Value Object在大多数时候是不可变的。

虽然不知道您正在处理哪个域,但很难回答您的问题。但通常地址不一定是值对象。 Eric Evans在他的book中提到邮政服务和交付路线域将地址视为实体。派出技术人员的电气公司需要意识到来自'123 Elm St'的两个服务电话实际上来自同一地址,它只需要派一名技术人员。在这种情况下,地址或“居住”是一个实体。

答案 2 :(得分:2)

汇总只关注数据修改。不应允许两个聚合修改相同的数据。由于值对象是不可变的,因此可以防止这种情况发生。因此,两个或多个聚合共享相同的值对象完全没有问题,因为它是一个只读数据结构,而聚合不关心读取模型。

Address a = new Address("1111 ABC Ave.");
person.setAddress(a);
letter.setAddress(a);

person.getAddress().change("2222 XYS Ave.") // THIS IS ILLEGAL SINCE Address is a VO (immutable)

地址永远不会发生,因此分享并不危险,因为你对人的地址做的任何事情都不会对信件产生影响,所以信件仍在保护它自己的不变量。

如果将Address作为一个实体,那么您将无法在两个实体中使用相同的地址,因为上述代码会使字母容易受到对人执行的更改的影响,并且会破坏边界,并且会阻止信件控制它的不变量。

这是Aggregate Roots的全部观点,它是一种限制副作用的模型。如果您定义了非常明确的修改边界,代码将更容易使用,并且您将防止潜在的有害意外影响。


我还要补充一点。正如在另一个答案中提到的那样,您希望不同的有界上下文具有不同的地址类型。原因是在一个上下文中您需要的地址细节不一定与您在另一个上下文中所需的细节相同。因此,通过使用两个Address类型,每个上下文一个,您可以将一个需求与另一个需求隔离开来。

说您需要的运输:

Address
{
    Number;
    Unit;
    Street;
    State;
    Country;
    PostalCode;
}

但是对于您需要的位置:

Address
{
    Number;
    Unit;
    Latitude;
    Longitude;
}

DDD会说,将它们称为地址,但将它们绑定到不同的上下文。因此,即使在语言中他们都被称为地址,他们的具体数据和行为可能会根据您所讨论的上下文而有所不同。您绝对不能做的是创建一种MonsterAddres,其中包含您域中所有上下文的所有可能数据和行为,并将其作为所有上下文中使用的Address类型。

请注意,我们在您的应用程序中讨论该模型,可以将所有地址数据存储在Monster地址表中,但在对App进行建模时,您应该将其分成映射到您的域的逻辑有界上下文。它所使用的无处不在的语言。