DDD 可分为战略和战术设计,其中战术设计是关于 DDD 的构建基块的。这篇文章将是关于DDD的基本构建基块:***实体和价值对象(VOs)。***实体和 VO 是 DDD 中表示模型的两个构建基块。它们是域驱动设计(服务和域事件除外)的起点。

entities_value-objects.png

除了只看实体和VO的定义,我还将为您提供一些如何区分它们的指导。

DDD 的构建块

在本章中,我将解释实体和 VO 的概念。DDD 中有更多的构建基块,如服务和域事件(见上图),但它们将超出此帖子的范围。

实体

埃里克 · 埃文斯在 Ddd 书中介绍了实体:

许多对象不是由它们的属性来根本定义的,而是由连续性和身份的线索来定义的。

这句话已经介绍了实体的主要特征:连续性(也通常被称为有生命周期)和身份

主要由其身份定义的对象称为实体。

当你想到一个实体时,想象一些需要随着时间推移而跟踪,其属性可能会随着时间而改变的东西。为了能够跟踪某物,您需要一种识别对象的方法,并在时间流逝后回答"这是同一个对象吗?一个非常强大的指标的东西是一个实体的东西是像属性(如,或)或属性前缀喜欢或。status``pending``active``inactive``current``last

即使不同对象的属性相同,也必须对对象进行区分。考虑一个申请,管理学生在大学注册不同的课程。A 将是同一个学生,如果属性喜欢,甚至改变。定义学生成为同一个学生意味着什么非常重要。也许这将是类似或只是一个通用的东西。下图显示了此应用程序的潜在类图,从现在起,我将以此为示例使用。Student``email``name``matriculationNumber``id

Entities.png

这可能是实体实际实例的数据库表示*(简单来说,课程承诺,因为它并不真正需要证明我的观点以后)*:Student

database_representation.png

您必须从域的角度回答**“是什么使对象成为同**一对象?因此,定义实体的身份需要了解该域。

警惕要求按属性匹配对象的要求。

这意味着像"如果属性相同,对象应该是相同的"这样的要求是一个强有力的指标,表明它可能根本不是一个实体。

实体也被称为**“参考对象**"。我喜欢这个术语,因为我脑子里有一个图像,指头(箭头)随着时间推移引用了同一个对象。

(在 JPA 中,注释用于实体。@Entity

值对象 (VOs)

许多对象没有概念性。这些对象描述了一个事物的一些特征

VO 没有身份。它们由属性而不是标识符定义。您可以将 VOs 视为实体的复杂属性。

表示没有概念标识的域的描述性方面的对象称为值对象。价值对象被刻例化,以表示我们只关心它们*是什么*,而不是它们是谁是谁的设计元素。

回想实体部分的学生示例。可以有一个,其中包括,和。在我们的应用程序领域, 地址将是一个 Vo 。更改其中一个属性将使它成为一个不同的地址。地址更像是一个复杂的属性类型(而不是原始数据类型,如字符串、酒类等)**实体(或其他 VO;**在此情况下为实体)。Student``Address``street``streetNumber``postcode``city``Student

entities-and-vo.png

不关心对象的身份让我们可以自由地简化设计并优化性能。我们现在可以共享 VOs 实例,因为它们本质上就像一个持有某些信息的复杂属性。要做到这一点**,VO必须是不变的**。更改 VOs 属性必须导致创建新实例,而不是修改现有实例。

再次,回到学生的例子。想起住在同一地址的两个学生。他们可以共享 VO 的相同实例,但如果其中一个学生移动到其他地方,我们不想改变该实例,因为这也会影响其他学生。相反,我们最终创建了一个新实例(或重复使用该地址的现有实例(如果它存在)。共享 VO 实例显示在下一张图像中。它还展示了 VO 就像一个复杂的属性,因为它最终在在这种情况下的数据库中的同一行。Address``Address

reference_vo.png

不要将人工 ID 连接到 VO. VO 也不会在数据库中自己的表中表示。如果你这样做,你不仅会失去性能的好处和较低的复杂性,但你也会混淆模型,使迫使所有对象进入相同的模具。

由于 VOs 的上述好处,您应该将 VO 作为默认值,并且仅在需要时将身份分配给某物(因此使其成为实体)。

(在 JPA 中,注释 (1:1) 和 (1:n) 用于 VOs。@Embeddable``@ElementCollection

区分实体和价值对象

这篇文章侧重于实体和 Vos, 因为它们是相似的, 但不同。我经常问自己的一个问题:“这是实体还是价值对象?两者都是对实际数据进行建模,并且通常都持续在数据库中。但正如我们已经了解到的,实体和 VOs 存在差异。

下表展示了实体和价值对象之间的主要区别:

Entities-Page-5.png

以下问题可以帮助识别实体或 VO:

*我可以用具有相同属性的另一个对象替换对象吗?*

**如果答案是肯定的,则它是一个值对象。**如前所述,VOs 就像一个复杂的数据类型。您也可以将原始数据类型视为 VO 的整数。那么,你的对象是不是像整数?

*我必须随着时间的推移跟踪一些事情吗?*

**如果答案是,则它是一个实体。**允许跟踪信息的实体。而 Vos 就像时间点的快照。

⚠️**如果某样东西是实体或 VO 取决于您的域。 *它可能是一个域内的实体,而另一个域中的 VO。因此,诸如"地址是价值对象"之类的陈述完全是错误的。一个例子可能是钱。在电子商务中,应用程序资金将像 VO 一样,因为它只代表购买某些东西的能力。100 美元是 100 美元。但是,在处理银行抢劫案时,您可能确实关心特定的钞票及其序列号,以独特的身份识别它。*(我想你🤪得到这个想法)

结论

实体和值对象是 DDD 的几个构建基块中的两个,它们属于 DDD 的战术设计部分。他们都有存储信息的目的,而且大多数情况下,它们最终被存入数据库,但存在差异,我指出。价值对象应该是您的选择,因为它们不那么复杂,并允许更好的性能。如果您需要身份概念,请与实体一起去。