实体和值对象

实体是什么

 

在 DDD 中有这样一类对象,它们拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致。对这些对象而言,重要的不是其属性,而是其延续性和标识,对象的延续性和标识会跨越甚至超出软件的生命周期。我们把这样的对象称为实体。

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

1,实体的业务形态

在战略设计时,实体是领域模型的一个重要对象。领域模型中的实体是多个属性、操作或行为的载体,在事件风暴中,我们可以根据命令、操作或者事件,找出产生这些行为的业务实体对象;

2,实体的代码形态

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

3,实体的运行形态

我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然是同一个实体

4,实体的数据库形态

值对象

领域建模的过程中,值对象可以保证属性归类的清晰和概念的完整性,避免属性零碎。

举个简单的例子,请看下面这张图:

 人员实体原本包括:姓名、年龄、性别以及人员所在的省、市、县和街道等属性。这样显示地址相关的属性就很零碎,现在,我们可以将“省、市、县和街道等属性”拿出来构成一个“地址属性集合”,这个集合就是值对象了

1,值对象的业务形态

本质上,实体是看得到、摸得着的实实在在的业务对象,实体具有业务属性、业务行为和业务逻辑。而值对象只是若干个属性的集合,只有数据初始化操作和有限的不涉及修改数据的行为,基本不包含业务逻辑。值对象的属性集虽然在物理上独立出来了,但在逻辑上它仍然是实体属性的一部分,用于描述实体的特征。在值对象中也有部分共享的标准类型的值对象,它们有自己的限界上下文,有自己的持久化对象,可以建立共享的数据类微服务,比如数据字典。

2,值对象的代码形态

值对象在代码中有这样两种形态,

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

 3,值对象的运行形态

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

 如果你对这两种方式不够了解,可以看看下面的例子。

案例 1:以属性嵌入的方式形成的人员实体对象,地址值对象直接以属性值嵌入人员实体中。

 案例 2:以序列化大对象的方式形成的人员实体对象,地址值对象被序列化成大对象 Json 串后,嵌入人员实体中。

 4,值对象的数据库形态

DDD 引入值对象是希望实现从“数据建模为中心”向“领域建模为中心”转变,减少数据库表的数量和表与表之间复杂的依赖关系,尽可能地简化数据库设计,提升数据库性能。

 如何理解用值对象来简化数据库设计呢?

传统的数据建模大多是根据数据库范式设计的。

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

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

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

我们可以综合这两个方案的优势,扬长避短。在领域建模时,我们可以把地址作为值对象,人员作为实体,这样就可以保留地址的业务涵义和概念完整性。而在数据建模时,我们可以将地址的属性值嵌入人员实体数据库表中,只创建人员数据库表。这样既可以兼顾业务含义和表达,又不增加数据库的复杂度。

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

强调一点,我们不避讳传统的设计方法,毕竟适合自己的才是最好的。

实体和值对象的区别

实体和值对象的目的都是抽象聚合若干属性以简化设计和沟通,有了这一层抽象,我们在使用人员实体时,不会产生歧义,在引用地址值对象时,不用列举其全部属性,在同一个限界上下文中,大幅降低误解、缩小偏差,两者的区别如下:


①两者都经过属性聚类形成,实体有唯一性,值对象没有。在本文案例的限界上下文中,人员有唯一性,一旦某个人员被系统纳入管理,它就被赋予了在事件、流程和操作中被唯一识别的能力,而值对象没有也不必具备唯一性。


②实体着重唯一性和延续性,不在意属性的变化,属性全变了,它还是原来那个它;值对象着重描述性,对属性的变化很敏感,属性变了,它就不是那个它了。


③战略上的思考框架稳定不变,战术上的模型设计却灵活多变,实体和值对象也有可能随着系统业务关注点的不同而更换位置。比如,如果换一个特殊的限界上下文,这个上下文更关注地址,而不那么关注与这个地址产生联系的人员,那么就应该把地址设计成实体,而把人员设计成值对象。

聚合根、实体、值对象的简单理解

在聚合的模型中包含聚合根、实体和值对象。聚合根在数据中相当于主表的概念,实体是一般的表,而值对象可以设计成一般表,但是大多数情况下可以依托引用的实体表设计成嵌入属性集或者以Json串的形式存储。
实体就是我们一般理解上的业务对象,我们关注他们的生命周期,所以他们会有全局ID,通过ID来管理追踪它的生命周期。而值对象主要是用于描述的属性集,我们不关注他们的生命周期,更关注它的属性值。比如对于人民币100元,作为发行方央行在判断他是否是假币时,会关注它的ID,通过ID来追踪它的流通轨迹和生命周期。而在流通领域,我们只关注它的价值,不同ID的人民币价值都是100。
为什么会这样设计?我个人感觉主要还是为了实现聚合的解耦。在管理这个实体的聚合中,我们需要通过ID来管理这个实体的生命周期,而当这个实体数据流转到其它聚合时,这个实体的数据值就不允许修改了。这样可以保证一份数据只在一个地方修改,而可以在多个不同的业务领域使用,保证业务的“高内聚和低耦合”。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当我们使用领域驱动设计(Domain-Driven Design)来设计软件系统时,实体(Entity)、对象(Value Object)、领域服务(Domain Service)和聚合根(Aggregate Root)是很重要的概念。 下面是这些概念的例子: - 实体(Entity):一个实体是具有唯一标识符的对象,它在系统中具有生命周期。例如,一个订单、一个用户或一辆车都可以被视为实体。对于同一实体,即使其属性发生变化,其唯一标识符也不会发生改变。 - 对象(Value Object):一个对象是没有唯一标识符的对象,它的属性描述了其本质和特性。例如,一个地址、一个日期或一种货币类型都可以被视为对象。它们的属性可以改变,但是它们的身份和身份标识不会改变。 - 领域服务(Domain Service):一个领域服务是一组相关的操作或功能,它们不属于任何一个特定的实体对象,而是跨越多个实体对象。例如,一个支付服务、一个地址验证服务或一个货币转换服务都可以被视为领域服务。 - 聚合根(Aggregate Root):一个聚合根是一组实体对象的集合,它们共同形成了一个有意义的整体。聚合根具有唯一标识符,并且负责维护其内部对象之间的一致性和完整性。例如,一个订单可以是一个聚合根,它包括订单项、顾客信息和付款信息等实体对象。在这种情况下,订单作为聚合根负责确保所有相关实体对象之间的一致性和完整性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值