2011-09-19 60 views
4

我想问关于DDD功能的问题。可以说我们有两个聚合,每个聚合包含值对象地址。根据埃里克埃文斯DDD,我们应该隔离彼此的聚合,所以第一个聚合的聚合根不能有一个链接到地址。坦率地说,这对我来说似乎没有意义,所以问题是如何解决这种情况?哪个聚合应该包含Address?DDD聚合和价值对象

谢谢

回答

9

您可以使用相同的值对象。但是,只有在聚合根存在于相同有界上下文中并因此对于两个聚合具有相同含义时才这样做。如果聚合存在于不同的有界上下文中,则有2个独立的并且重复。埃里克正在试图与一个有限的背景相关的问题泄漏到另一个问题上。

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

希望这有助于

+0

您能举一个有界和无界的上下文的例子吗?为了更好的理解。 – madcyree

+1

如果你有一个有序的上下文和一个客户有界的上下文,你想拥有独立的地址。客户和订单AR位于不同的环境中。如果您有订单和运输信息AR,它们位于相同的上下文中,并且您可以使用相同的地址值对象。 –

+0

谢谢你的解释:) – madcyree

8

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

由于它没有概念标识,所以不能'引用'或'有链接'。你只能'包含'它。比方说你有一个用户和用户有年龄。年龄是一个价值对象。如果约翰25岁,简也25岁,他们不会“参考”同一个年龄段。乔恩的年龄简直等于简氏的年龄。所以如果你的地址确实是一个价值对象,那么你并没有违反任何聚合边界。您的聚合根简单地拥有相同的地址。即使你在技术上具有对地址的java/c#引用,它并不重要,因为值对象大多数时间是不可变的。

虽然不知道你在做什么域名,但很难回答你的问题。但通常地址不一定是一个价值对象。埃里克埃文斯在他的book中提到邮政服务和传递路径域将把地址视为一个实体。派出技术人员的电气公司需要认识到'123 Elm St'的两个服务电话实际上来自同一个地址,并且只需要派一名技术人员。在这种情况下,地址或'住宅'是一个实体。

2

聚合仅与数据修改有关。不应该允许两个聚合修改相同的数据。由于Value对象是不可变的,所以它可以防止这种情况发生。因此,两个或多个聚合共享相同的值对象是完全正确的,因为它是只读数据结构,聚合不关心读取模型。

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 
{ 
    Number; 
    Unit; 
    Street; 
    State; 
    Country; 
    PostalCode; 
} 

但对于位置,你需要:

Address 
{ 
    Number; 
    Unit; 
    Latitude; 
    Longitude; 
} 

DDD会说,叫他们两个地址,但它们绑定到不同的情境。因此,尽管在语言中他们都被称为地址,但他们的具体数据和行为可能会因您所谈论的环境而有所不同。你绝对不应该做的是创建一种MonsterAddres,它将包含域中所有上下文的所有可能的数据和行为,并且这是所有上下文中使用的地址类型。请注意,我们正在讨论应用程序中的模型,可以将所有地址数据存储在怪物地址表中,但是在为应用程序建模时,应该将其分隔为映射到您的域的逻辑有界上下文以及它所使用的无处不在的语言。