什么是 DDD
领域层:从业务逻辑开始下手,不考虑数据库的实现,将原本的 Service 拆成领域层和应用层
应用层:应用层是领域层的上层,依赖领域层,是各聚合的协调和编排,原则上是不包括任何业务逻辑。它以较粗粒度的封闭为前端接口提供支持。除了提供上层调用外,还可以包括事件和消息的订阅。
用户接口层:用户接口层面向用户访问的数据入向接口,可按不同场景提供不一样的用户接口实现。面向Web的可使用http restful的方式提供服务,可增加安全认证、权限校验,日志记录等功能;面向微服务的可使用RPC方式提供服务,可增加限流、熔断等功能。
基础设施层:基础设施层是数据的出向接口,封装数据调用的技术细节。可为其它任意层提供服务,但为了解决耦合的问题采用了依赖倒置原则。其它层只依赖基础设施的接口,于具体实现进行分离
总体结构:
分层实践
领域层
- 实体对象:具备唯一表示,能单独存在而且可以变化的对象
- 值对象:不能单独存在或在逻辑层面上存在无意义,且不可变化的对象
- 聚合:多个地偶像的集合,对外是一个整体
- 聚合根:聚合中可代表整个业务操作的实体对象,通过它提供对外访问操作,它维护聚合内部的数据一致性,它是聚合中对象的管理者(例如订单和订单项,只能通过订单对象来改订单项,不能通过 setter 来修改
贫血模型
例如:钱包和交易,还是原来的实体类型
此种模型下领域对象的作用很简单,只有所有属性的 get/set 方式,以及少量简单的属性值转换,不包含任何业务逻辑,不关系对象持久化,只是用来做为数据对象的承载和传递的介质。
而真正的业务逻辑则由领域服务负责实现,此服务引入持久化仓库,在业务逻辑完成之后持久化到仓库中,并在此可以发布领域事件 (Domain Event)
优点: 结构简单,职责单一,相互隔离性好,使用单例模型提高运行性能
缺点: 对象状态与行为分离,不能直观地描述领域对象。行为的设计主要考虑参数的输入和输出而非行为本身,不太具有面向对象设计的思考方式。行为间关联性较小,更像是面向过程式的方法,可复用性也较小。
SpringBoot 采用单例模式,尽量不手动创建对象,对象无状态化,故较推荐使用贫血模型
充血模型
“对象自己知道自己该怎么做事”,而不是被人(Service)摆布。
就是把 dao 的操作放在了 entity 当中
领域服务
/**
* Description: 交易服务
*/
public interface TradeService {
TradeRecord recharge(TradeRecord tradeRecord);//充值
TradeRecord consume(TradeRecord tradeRecord);//消费
}
- 先定义服务接口,接口的定义需要遵循现实业务的操作,切勿以程序逻辑或数据库逻辑来设计定义出增删改查
- 主要的思考方向是交易对象对外可提供哪些服务,这种服务的定义是粗粒度且高内聚的,切勿将某些具体代码实现层面的方法定义出来
- 接口的输入输出参数尽量考虑以对象的形式,充分兼容各种场景变化
- 关于前端需要的复杂查询方法可不在此定义,一般情况下查询并非是一种领域服务且没有数据变化,可单独处理
- 领域服务的实现主要关注逻辑实现,切勿包含技术基础类代码,比如缓存实现,数据库实现,远程调用等
基础设施接口
定义接口TradeRepository
- 基础设施接口放在领域层主要的目的是减少领域层对基础设施层的依赖
- 接口的设计是不可暴露实现的技术细节,如不能将拼装的SQL作为参数
应用层实现
是一个Manager 然后注入Service
还有发消息的功能?
应用服务:
- 应用层是很薄的一层,主要用于调用和组合领域服务,切勿包含任何业务逻辑
- 可包括少量的流程参数判断
- 由于可能是多个领域服务组合操作调用,如果存在原子性要求可以增加 @Transactional事务控制
事件订阅:
- 事件订阅是进程内多个领域操作协作解耦的一种实现方式,它也是进程内所有后续操作的接入口
- 它与应用服务的组合操作用途不一样,组合是根据场景需求可增可减,但事件订阅后的操作是相对固化的,主要是满足逻辑的一致性要求
- TransactionPhase.AFTER_COMMIT配置是在前一操作事务完成后再调用,从而减少后续操作对前操作的影响
- 事件订阅可能会有多个消息主体,为了方便管理最好统一在一个类里处理
- MQ消息发布一般放在事件订阅中
消息订阅:
- 消息订阅是多个微服务间协作解耦的异步实现方式
- 消息体尽量以统一的对象包装进行传递,降低对象异构带来的处理难度
基础设施层
dao,redis等接口的封装
用户接口层
controller web和grpc都在这里
其它相关内容
CQRS 是一种与领域驱动设计 (DDD) 和事件溯源相关的架构模式。Greg Young 在 2010 年创造了这个术语,CQRS 的内容基于 Bertrand Meyer 的 CQS 设计模式。但它的背后是什么?
CQS (命令查询分离)设计模式建议将对象的方法映射到两类:方法要么改变对象的内部状态,但不返回任何内容,要么只返回元数据。这种方法称为Command。或者一个方法返回信息但不改变内部状态。这种方法称为Query。