Domain Driven Design
(一)Domain Primitive
DP
Primitive 的定义是:不从任何其他事物发展而来,初级的形成或生长的早期阶段。类比Java中的int,string
Domain Primitive 是一个在特定领域里,拥有精准定义的、可自我验证的、拥有行为的 Value Object 。
案例:
接口语义、参数校验
核心业务逻辑清晰度
单元测试可行性
DP:
三原则
领域:业务问题的范畴
将一个大的业务划分成多个领域
案例:
注册——输入客户姓名、手机号,根据手机号的归属地和所属运营商,将客户群体分组。
流程:
输入用户名、手机号,校验参数,获取手机号的归属地和运营商编号,获取分组编号,构建客户对象存入数据表。
归属地、运营商是查询出的,再查询出分组编号。
数据表存入的是姓名、手机号、分组编号,并不需要归属地和运营商。
修改目标:
- 接口语义明确,可扩展性强,一定的自检性
- 参数校验逻辑复用,内聚
- 参数校验异常和业务逻辑异常解耦
自定义PhoneNumber类
核心业务逻辑清晰度
业务:注册
但是,注册中包括获取归属地编码和运营商编号,使得业务并不纯粹。
胶水逻辑
改造:
根据手机号获取归属地和运营商编号,应该放在PhoneNumber类中
注册中只存在2个最纯粹的操作:获取用户信息(即分组编号)、存入数据表
传统的POJO中,类中只包含属性和get、set方法,此时的PhoneNumber却包含了初始化、校验、属性处理等多种逻辑。
PhoneNumber这个类型成为DP
DP三原则:
让隐性的概念显性化
让隐性的上下文显性化
封装多对象行为
隐性的概念显性化:归属地编码、运营商编码,是PhoneNumber事物的隐性属性。
隐性的上下文显性化:PhoneNumber加入protocal区号协议。相同的手机号在不同的区号下实际是不同的。
封装多对象行为:一个DP可以封装其他多个DP的行为。
案例
解耦外部依赖:数据访问改造、RPC调用改造
解耦内部逻辑
优化单元测试
Entity
Repository
Domain Service
案例新增业务
1.对手机号实名验证,实名信息需要调用外部服务
2.根据外部返回的实名信息,按照一定的逻辑计算出用户标签,记录在用户账号中
3.根据用户标签,为该用户开通相应等级的新客福利
校验参数的合法性和一致性
合法性:位数、数字等
一致性:调用远程查询是否一致,比如实名认证。
流程设计:
1.输入客户姓名、手机号,根据手机号调用外部服务查询实名信息,是否和入参的姓名一致
2.根据实名信息,按照一定的逻辑计算该用户的标签,作为用户的一个属性
3.根据手机号的归属地和运营商,查询出销售组信息,销售ID作为用户的一个属性
4.构建用户对象和福利对象,查询风控是否通过
5.若通过,用户失去新客身份,且可以查到福利信息,数据落库;若不通过,用户保持新客身份,但查询不到福利信息,数据落库
6.上述逻辑在同一个事务中处理
外部依赖:业务域核心之外的,对其没有控制权。想要,即使外部依赖发生变化,也能将本业务域代码的变动控制在最小范围内。
可维护性:由外部依赖的变化,所导致的内部系统的改造程度,可以理解为一个系统的可维护性。
DO类,数据库schema
面向具体实现编程 改成 面向抽象接口编程
rpc调用抽象
使用realnameService接口类,替代了telecomRealnameService具体实现类
具体实现对象,通过配置注入
达到依赖倒置的目的
使用realnameInfo这个DP,代替了telecomInfoDTO这个具体实现
将变动控制在具体实现类和配置文件内部
防腐层:防止外部系统的腐烂,影响到了自己的系统
realnameService是防腐层
数据访问抽象
Entity:有状态,领域实体
DP:无状态,组成实体的基础类型
业务逻辑不应该耦合数据访问的具体实现,于是出现了repository,是数据访问的抽象层
可扩展性:由内部逻辑的变化,所导致的内部系统的改造程度。
奖品对象、风控检查耦合在了注册逻辑中
更改注册逻辑:获取用户信息、检查并更新用户、存储用户信息
domain service:封装多Entity或跨业务域的逻辑
聚合根
聚合外部可以持有根对象的引用来操作聚合
聚合内部通过执行一系列逻辑来保持各个对象的状态一致