DDD—实体和值对象

一、实体
     实体是领域模型中的一个对象,带有业务含义的对象,集多个业务属性,业务行为于一体。领域建模时,我们根据业务场景分析,找到跟业务逻辑相关的实体对象,然后按照实体间的关联将多个对象进行聚合。
     实体最大的特点是拥有唯一标识符,这个标识符贯穿整个软件的生命周期,不随业务流程和状态变更后更改,在领域模型中以领域对象DO的形式存在。
     如下代码示例:人事领域里的人员考勤子域,考勤里面有员工对象,员工需要通过上班打卡,下班打卡完成一个完整的考勤行为,这个员工就是一个实体,因为每个人在公司都有工号,是标识这个人的唯一ID,不管他去上海还是北京出差打卡,他的员工ID都不会变化。
  同时实体还会包含跟考勤相关的上班打卡和下班打卡方法,是一个充血模型。
/**
 * 人员实体
 * @author test11
 */
public class Person {

    //人员id
    private String id;

    //姓名
    private String name;

    //地址
    private Address address;

    //上班打卡
    private void goWork(){

    }

    //下班打卡
    private void leaveWork(){

    }

}
 
二、值对象
   如上代码示例中,员工的信息由人员id,姓名,所在的省,市,街道组成,我们可以将省,市,街道抽象出来一个Address,构成一个地址的属性集合,这个集合的名称就是地址值对象。
   所以值对象就是一个属性集合,将不同的关联属性组合成了一个概念整体,具有整体概念和不可修改的特性。
/**
 * 地址值对象
 */
public class Address {

    //省份
    private String province;
    //城市
    private String city;
    //街道
    private String street;
}
 
三、实体和值对象
   实体和值对象都是微服务底层最基础的领域对象,实现领域最基本的业务逻辑。
   实体和值对象都是若干属性的集合,实体一般是带有业务含义的的对象,具有业务属性,业务行为和业务逻辑。
   值对象也是若干属性的集合,但他只有数据初始化操作,不涉及数据修改,基本不包含业务逻辑。
      值对象是属于实体的一部分,如果值对象是单一属性,则直接定义为实体的属性,如果值对象是属性集合,则将他设计为值对象类,值对象没有ID,会被实体整体引用。

  

 

四、实体和值对象的数据库形态
  实体:将领域模型映射到数据模型时,一个实体(DO)可能对应0,1,多个数据库持久化对象(PO),大多数情况下DO和PO是一对一的关系,当DO只是暂住内存时,也可以不需要持久化。当用户和角色两个实体合并成权限实体时,一个DO则对应了多个PO。当用户实体和订单实体合并到一张表中时,这两个DO的生成需要一个PO的拆分,这是多对一的关系。
  值对象:值对象的数据库设计大都采用了反范式,实体对象的属性值和值对象的属性值集合以JSON形式保存在同一个数据库表中。
  上面的人员和地址的对应关系,设计数据库表时可以有三种方式:
  (1)把地址值对象的所有属性加到人员实体表中:这种设计方式会破坏地址的业务含义和属性完整
   

  (2)创建人员实体的表,关联地址实体表:增加了不必要的实体和表

    

  (3)实体对象的属性值和值对象的属性值集合以JSON形式保存在同一个数据库表中:数据建模时,可以将值对象的属性集合嵌入到实体表中,保留对象的业务含义,同时减少了表的设计,一般是将值对象序列化成大对象JSON串后,嵌入到实体表中的

     

 

  基于第三种方式的领域模型落地,现在许多数据库都开始支持基于JSON串的CRUD操作了,当然,类似这样值对象的设计,多了可能会使实体堆积一堆缺乏完整意义的属性。

  但是值对象不可变,在并发环境下获取的永远是相同的对象,不会被修改,所以值对象可以被多个实体并发引用,所以高并发场景下的领域对象一般优先设计为值对象而非实体,可以保证线程安全。

  值对象还有一个重要作用,以数据冗余的方式记录业务发生那一刻的数据,比如用户聚合中包含了用户(聚合根,也是实体)和地址值对象,订单生成那一刻,用户和地址信息以JSON的方式冗余进订单数据中,这样避免了订单聚合每次都通过调用用户的聚合根ID获取最新用户信息和地址,解耦了用户聚合和订单聚合,同时用户实体和地址值对象也以冗余的方式,记录了业务的快照数据,还原业务发生前后的场景。

五、贫血模型和充血模型
    贫血模型:Spring的bean就是一种贫血模型,领域被用来作为属性存储的载体,而没有实现具体方法,比如教育领域的校长,只会记录他的年龄,职级,工作年限,而不会有他的一些行为的方法实现,单单用来作为属性的存储。
    充血模型:实体不止包含对象的属性,也包含他对应的行为的方法实现,而不止仅仅作为属性存储的载体。
 
  

    参考书籍 ——《基于DDD和微服务的中台架构与实现》欧创新、邓頔
    参考书籍 ——《领域驱动设计》Eric Evans
    参考书籍 ——《架构真经》Martin L. Abbott

  
posted @ 2021-05-28 23:23  纪煜楷  阅读(2282)  评论(0编辑  收藏  举报