Domain Driven Design
Domain Driven Design
参考文献:JD 云方案
贫血/失血模型
充血模型
由于过去 ER 模型以及主流 ORM 框架的发展,让很多开发者对实体的概念还停留在与关系形数据库映射这个层面。从而导致实体只有空洞的属性,而实体的业务逻辑散落各个 service、util、helper、handler 等各种角落中。这种现象就被称为贫血模型现象。
如何判断自己的工程是否有贫血模型现象?
1、大量的 XxxDO 或者 Xxx:实体对象只包含与数据库表映射的属性,没有行为或者及其少量的行为;
2、业务逻辑在各种 service、controller、util、helper、handler 中:实体的业务逻辑散落在不同层级、不同类、不同方法中,相似场景有大量的重复代码。
3.4 为什么贫血模型不好?
无法保证实体对象的完整性和一致性:贫血模型下,实体属性的状态和值只能由调用方保证,但是属性的 get 和 set 是公开的,因此所有调用方都可以调用。所以无法保证对象的完整性和一致性。
操作实体对象的边界很难发现:由于对象只有属性,属性的边界值、调用范围不受实体自身控制,各个地方都可以调用,边界值和范围也只能由调用方自行保障。如果实体的边界值有所变化,那么所有调用方都需要调整,这种情况下很容易导致 bug 的产生。
强依赖底层:贫血模型下的实体和数据库模型映射、协议等。因此如果底层改变,那么上层逻辑需要全部跟着改变。“软件”变成了“固件”。
总结一句话:贫血模型下,软件的可维护性、可扩展性、可测试性极差!
使用 repository 之后,数据模型和领域模型都各司其职。通过 Assembler 和 Converter 进行模型之间的转换。
在代码中,动态转换映射 VS 静态转换映射
虽然 Assembler/Converter 是非常好用的对象,但是当业务复杂时,手写 Assembler/Converter 是一件耗时且容易出 bug 的事情,所以业界会有多种 Bean Mapping 的解决方案,从本质上分为动态和静态映射。
动态映射方案包括比较原始的 BeanUtils.copyProperties、能通过 xml 配置的 Dozer 等,其核心是在运行时根据反射动态赋值。动态方案的缺陷在于大量的反射调用,性能比较差,内存占用多,不适合特别高并发的应用场景。而 BeanUtils 等 copy 类工具隐藏了内部 copy 的过程,很容易引发 bug 且不易排查。
MapStruct 通过注解,在编译时静态生成映射代码,其最终编译出来的代码和手写的代码在性能上完全一致,且有强大的注解等能力。会节省大量的成本。
根本原因就是,大部分的开发人员混淆了数据模型和领域模型这两个概念。
数据模型(Data Model):数据模型解决的是数据如何持久化、如何传输的问题;
领域模型(Domin Model):领域指的是某一个独立的业务领域或者问题空间,领域模型就是解决这个业务领域或者问题空间而设计的模型;解决的是业务领域的问题。
在 DDD 中,repository 就是用于区分数据模型和领域模型提出来的概念。
