Skip to content

Commit

Permalink
实体和值对象
Browse files Browse the repository at this point in the history
  • Loading branch information
studeyang committed Sep 9, 2024
1 parent 2563704 commit 408e11d
Showing 1 changed file with 43 additions and 10 deletions.
53 changes: 43 additions & 10 deletions C类/C05-DDD/[极客时间]-DDD实战课-01基础篇.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ DDD 是一种处理高度复杂领域的设计思想,它试图分离技术实

我们不妨来看看 DDD 是如何进行战略设计的。

![image-20211109222811532](https://technotes.oss-cn-shenzhen.aliyuncs.com/2021/images/20211109222811.png)
<img src="https://technotes.oss-cn-shenzhen.aliyuncs.com/2021/images/20211109222811.png" alt="image-20211109222811532" style="zoom: 50%;" />

第一步:在事件风暴中梳理出领域实体等领域对象。

Expand Down Expand Up @@ -202,16 +202,35 @@ DDD 的研究方法与自然科学的研究方法类似。当人们在自然科

**实体**

1. 实体的业务形态:在战略设计时,实体是领域模型的一个重要对象。领域模型中的实体是多个属性、操作或行为的载体。
2. 实体的代码形态:在代码模型中,实体的表现形式是实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。
3. 实体的运行形态:实体以 DO(领域对象)的形式存在,每个实体对象都有唯一的 ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然是同一个实体。
4. 实体的数据库形态:在领域模型映射到数据模型时,一个实体可能对应 0 个、1 个或者多个数据库持久化对象。大多数情况下实体与持久化对象是一对一。在某些场景中,有些实体只是暂驻静态内存的一个运行态实体,它不需要持久化。
在 DDD 中有这样一类对象,它们拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致。对这些对象而言,重要的不是其属性,而是其延续性和标识,对象的延续性和标识会跨越甚至超出软件的生命周期。我们把这样的对象称为实体。

在 DDD 不同的设计过程中,实体的形态是不同的。

1. 实体的业务形态

领域模型中的实体是多个属性、操作或行为的载体。在事件风暴中,我们可以根据命令、操作或者事件,找出产生这些行为的业务实体对象,进而按照一定的业务规则将依存度高和业务关联紧密的多个实体对象和值对象进行聚类,形成聚合。你可以这么理解,实体和值对象是组成领域模型的基础单元。

2. 实体的代码形态

在代码模型中,实体的表现形式是实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。在 DDD 里,这些实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在实体类的方法中实现,跨多个实体的领域逻辑则在领域服务中实现。

3. 实体的运行形态

实体以 DO(领域对象)的形式存在,每个实体对象都有唯一的 ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然是同一个实体。比如商品是商品上下文的一个实体,通过唯一的商品 ID 来标识,不管这个商品的数据如何变化,商品的 ID 一直保持不变,它始终是同一个商品。

4. 实体的数据库形态

与传统数据模型设计优先不同,DDD 是先构建领域模型,针对实际业务场景构建实体对象和行为,再将实体对象映射到数据持久化对象。

在领域模型映射到数据模型时,一个实体可能对应 0 个、1 个或者多个数据库持久化对象。大多数情况下实体与持久化对象是一对一。在某些场景中,有些实体只是暂驻静态内存的一个运行态实体,它不需要持久化。比如,基于多个价格配置数据计算后生成的折扣实体。

**值对象**

值对象相对实体来说,会更加抽象一些,概念上我们会结合例子来讲。

值对象是 DDD 领域模型中的一个基础对象,它跟实体一样,都包含了若干个属性,它与实体一起构成聚合。

![image-20220124224428629](https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124224428.png)
![image-20240909234910594](https://technotes.oss-cn-shenzhen.aliyuncs.com/2024/202409092349699.png)

如上图所示。我们可以将“省、市、县和街道等属性”拿出来构成一个“地址属性集合”,这个集合就是值对象。

Expand All @@ -223,20 +242,26 @@ DDD 的研究方法与自然科学的研究方法类似。当人们在自然科

我们看一下下面这段代码,person 这个实体有若干个单一属性的值对象,比如 Id、name 等属性;同时它也包含多个属性的值对象,比如地址 address。

![image-20220124224851540](https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124224851.png)
<img src="https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124224851.png" alt="image-20220124224851540" style="zoom:67%;" />

3. 值对象的运行形态。

实体实例化后的 DO 对象的业务属性和业务行为非常丰富,但值对象实例化的对象则相对简单。

值对象嵌入到实体的话,有这样两种不同的数据格式,也可以说是两种方式,分别是属性嵌入的方式和序列化大对象的方式。

![image-20220124225147082](https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124225147.png)
<img src="https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124225147.png" alt="image-20220124225147082" style="zoom:50%;" />

![image-20220124225205733](https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124225205.png)
<img src="https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124225205.png" alt="image-20220124225205733" style="zoom:50%;" />

4. 值对象的数据库形态。

举个例子,还是基于上述人员和地址那个场景,实体和数据模型设计通常有两种解决方案:第一是把地址值对象的所有属性都放到人员实体表中,创建人员实体,创建人员数据表;第二是创建人员和地址两个实体,同时创建人员和地址两张表。

第一个方案会破坏地址的业务涵义和概念完整性,第二个方案增加了不必要的实体和表,需要处理多个实体和表的关系,从而增加了数据库设计的复杂性。

那到底应该怎样设计,才能让业务含义清楚,同时又不让数据库变得复杂呢?

在领域建模时,我们可以将部分对象设计为值对象,保留对象的业务涵义,同时又减少了实体的数量;在数据建模时,我们可以将值对象嵌入实体,减少实体表的数量,简化数据库设计。

5. 值对象的优势和局限。
Expand All @@ -249,10 +274,18 @@ DDD 提倡从领域模型设计出发,而不是先设计数据模型。

传统的数据模型设计通常是一个表对应一个实体,一个主表关联多个从表,当实体表太多的时候就很容易陷入无穷无尽的复杂的数据库设计,领域模型就很容易被数据模型绑架。可以说,值对象的诞生,在一定程度上,和实体是互补的。

有些场景中,地址会被某一实体引用,它只承担描述实体的作用,并且它的值只能整体替换,这时候你就可以将地址设计为值对象,比如收货地址。而在某些业务场景中,地址会被经常修改,地址是作为一个独立对象存在的,这时候它应该设计为实体,比如行政区划中的地址信息维护。
我们还是以前面的图示为例:

<img src="https://technotes.oss-cn-shenzhen.aliyuncs.com/2022/learn/20220124224428.png" alt="image-20220124224428629" style="zoom:67%;" />

在领域模型中人员是实体,地址是值对象,地址值对象被人员实体引用。在数据模型设计时,地址值对象可以作为一个属性集整体嵌入人员实体中,组合形成上图这样的数据模型;也可以以序列化大对象的形式加入到人员的地址属性中,前面表格有展示。

从这个例子中,我们可以看出,同样的对象在不同的场景下,可能会设计出不同的结果。有些场景中,地址会被某一实体引用,它只承担描述实体的作用,并且它的值只能整体替换,这时候你就可以将地址设计为值对象,比如收货地址。而在某些业务场景中,地址会被经常修改,地址是作为一个独立对象存在的,这时候它应该设计为实体,比如行政区划中的地址信息维护。

# 05 | 聚合和聚合根:怎样设计聚合?

在事件风暴中,我们会根据一些业务操作和行为找出实体(Entity)或值对象(ValueObject),进而将业务关联紧密的实体和值对象进行组合,构成聚合,再根据业务语义将多个聚合划定到同一个限界上下文(Bounded Context)中,并在限界上下文内完成领域建模。

**聚合**

在 DDD 中,实体和值对象是很基础的领域对象。实体一般对应业务对象,它具有业务属性和业务行为;而值对象主要是属性集合,对实体的状态和特征进行描述。
Expand Down

0 comments on commit 408e11d

Please sign in to comment.