5 值对象简化DB的最佳实践
传统数据建模大多根据数据库范式设计,每个数据库表对应一个实体,每个实体的属性值用单列存储,一个实体主表会对应N个实体从表。
而值对象简化了DB设计,多采用反范式,值对象的属性值和实体对象的属性值保存在同一DB实体表。
比如人员和地址,要设计实体和数据模型,有如下解决方案:
把地址值对象的所有属性放入人员实体表,创建人员实体、人员数据表
会破坏地址的业务含义和概念完整性
创建人员和地址两个实体,同时创建人员和地址两张表
增加了不必要的实体和表,需要处理多个实体和表的关系,导致数据库复杂性剧增
有没有一种设计可使得业务含义清晰,又不让数据库变复杂?综合以上方案优势,扬长避短:
领域建模时,把地址作为值对象,人员作为实体,即可保留地址的业务含义和概念完整性
数据建模时,将地址的属性值嵌入人员实体数据库表,只创建人员数据库表。这既可兼顾业务含义和表达,又不会复杂化DB
值对象就是通过该方式,简化DB设计:
领域建模时,将部分对象设计为值对象,保留对象的业务含义,同时又减少了实体数量
数据建模时,我们可以将值对象嵌入实体,减少实体表的数量,简化DB设计
要发挥对象的威力,就需优先领域建模,弱化DB作用,只把DB作为一个保存数据的仓库。即使违反DB设计原则,也不必大惊小怪,只要业务能顺利运行,无伤大雅。
分析
虽然优势是可简化DB复杂度。但若使用不当,优势就会成劣势。所以必须理解值对象的适用场景。
值对象采用序列化大对象的方式简化DB设计,减少实体表的数量,可简单、清晰表达业务概念。该方式虽然降低DB设计复杂度,却无法满足基于值对象的快速查询,导致搜索值对象的属性值变难。
值对象采用属性嵌入的方式提升了DB性能,但若实体引用的值对象过多,则会导致实体堆积一堆缺乏概念完整性的属性,这样值对象就会失去业务含义,操作也不方便。
所以对照优劣势并结合实际业务场景,才能发挥值对象的最大作用。
6 实体 V.S 值对象
主要区别如下:
- 实体有唯一性,值对象没有。比如用户具有唯一性,一旦某用户被系统管理,它就被赋予了在事件、流程和操作中被唯一识别的能力
- 实体着重唯一性和延续性,不在意属性的变化,属性全变了,它还是自己;值对象着重描述性,对属性变化敏感,属性变了,它就不是自己了
实体和值对象也可能随着系统业务关注点的不同而更换位置。比如,如果另一个限界上下文更关注地址,而不关注与这个地址产生联系的人员,那就把地址设计成实体,人员设计成值对象
比如多人的单位地址是一样的,怎么处理:
许多人可能属同一地址
许多地址也可能属同一人
所以人和地址既可分别作为实体而把对方作为值对象,也可共同作为实体描述业务,这正是业务设计的意义,而不是非黑即白。
DDD提倡从领域模型设计出发,而非先设计数据模型。
传统数据模型设计通常一个表对应一个实体,一个主表关联多个从表,当实体表太多,就很容易陷入复杂DB设计,领域模型就很容易被数据模型绑架。
在领域模型中人员是实体,地址是值对象,地址值对象被人员实体引用。
设计数据模型时
地址值对象可作为一个属性集整体嵌入人员实体
也可以序列化大对象的形式加入人员的地址属性
同样一个对象在不同场景,可能设计不同:
地址会被某一实体引用,只描述实体,并且其值只能整体替换,这时就可将地址设计为值对象,比如收货地址
地址会被经常修改,地址作为一个独立对象存在,这时应设计为实体,比如行政区划中的地址信息
参考
实体和值对象:从领域模型的基础单元看系统设计
《实现领域驱动设计》