前言
值对象是状态不可变的、可整体替换的、用于度量和描述领域中某件东西的对象。在落地DDD过程中我们常常遇到一个概念到底是建模成实体好还是建模成值对象好这种问题,其实各DDD大神早已经给出了答案,prefer value object to entity!
值对象建模
0、值对象不包含改变属性的方法
- 值对象里没有改变自身状态的方法,只有查询方法,根据迪米特法则和告诉而非询问原则决定查询方法是public还是protected
- 想要改变值对象状态,就新建一个值对象过来替换
1、值对象要包含领域逻辑
- 值对象虽然是不可变的,但是值对象本身可以利用自己的属性去完成一些领域逻辑,而且建议把领域逻辑写到值对象里。
- 实体尽量作为值对象的对外接口
2、迪米特法则
- 迪米特法则要求一个对象对另一个对象内部结构知道的越少越好,在代码体现上就是一个聚合根实体内部的值对象的属性和方法尽量都是protected以上级别的,这样包外部就没办法访问到实体内部的值对象,只有实体可以调用值对象的方法,值对象想要对外提供服务必须通过聚合根实体的代理。这种实现是符合高内聚低耦合原则的!但是需要在聚合根实体中实现大量的代理代码。
3、告诉而非询问原则
- 告诉而非询问原则在迪米特法则基础上有放宽值对象的访问级别。即值对象可以对外提供用于查询的public方法,而对对象(此处指聚合跟实体中引用的其他实体对象,非值对象)状态有修改的方法则被修饰为protected。有时我们需要值对象返回一个collection类型的属性,这个原则要求我们暴露的是不可改变的属性,比如Collections.unmodifyed(map),或者利用新建一个副本对象的方式提供返回。
在落地实践中,以上两个原则都是可以落地的,区别在于聚合根实体是否直接代理值对象的方法,这也决定了聚合根实体中代理方法代码量的多少。在落地过程中可以根据自己的实际需要进行选择。