概述
上篇战略设计产出了领域及问题域领域模型;详见:DDD领域驱动设计系列-原理篇-战略设计-CSDN博客
战术设计篇聚焦如何落地,包含实际解决方案模型落地,架构分层(Clean,CQRS),Reposity模式落地实践(如何维护聚合);
战略设计输入
领域划分
划分4大域:结算用户域,计费,清算,结算;
问题域领域模型
战术设计-解决方案模型
解决方案模型可以从性能及实际落地因素来考虑;在结算中由于实际要通过接收交易确认收货消息来触发结算,出于保存结算时的原始数据快照便于后续有据可依这里抽出结算收单模型;
这样最终的解决方案模型如下;
战术设计-架构分层
分层架构上是采用传统MVC还是DDD的六边型,Clean架构或者洋葱架构?
结论是:使用洋葱架构,原因:以领域实体为核心沉淀业务逻辑,外层依赖内层;架构上分为application service(业务流程编排),domain service(域服务),domain实体,以及入口服务&基础设施及外部业务依赖;
这样设计好处:1、基础设施变化不影响上层业务逻辑代码;2、更易理解维护及业务逻辑复用:业务逻辑沉淀在领域实体;
架构结果
各分层架构介绍
MVC
controller,service及dao层;缺点:1、所有的业务逻辑都写在service,复用度及可理解性不够;2、service直接依赖于Dao,当dao层实现有变化导致service变化;;
六边型
六边型核心思想是抽离业务入口为适配器,入口适配器依赖于应用核心接口;同时应用核心依赖于基础设施及外部业务系统接口;基础设施实现依赖于应用核心;
这样好处较MVC架构来讲当基础设施实现变化不会影响应用核心,eg:消息由rabbitMq切换成rocketMq,上层应用核心代码不用变;
Clean架构
和六边型思路一致:分离基础设施&外部依赖;区别是明确定义了以领域实体为核心,外层是服务;
洋葱架构
更进一步定义了域服务及流程服务application service;基础逻辑与上述六边型&clean架构一致:基础设施&外部依赖可变化但不影响应用核心逻辑代码;
战术设计-CQRS
传统我们查询也会经过applicationService及domainService,但有时查询需要的内容不仅仅是领域模型此时就会导致领域模型耦合了和领域无关的内容;如订单列表展示需要商户额外的地点信息;
这里使用CQRS(Command Query Responsibility Segregation)架构模式;
查询方面直接由Api层查询reposity,减少中间层次转换&减少查询逻辑对于领域模型的入侵;
战术设计-Reposity模式使用
为什么要用Reposity模式?
原因:1、解决传统service直接依赖Dao层导致Dao如果变化影响service代码大量改动;2、领域模型与持约化DO本身不是一对一等价关系,如合约领域模型实体Entity对应存储时涉及合约DO及合同条款DO;
Reposity分层架构
Domain Service层做为需求方负责制定需要的reposity存储接口,由Repsoity模块来进行实现;Reposity在实现的时候可对一个模型对象拆成多个DO存储或多个模型合成一个DO;
多个模型Entity对应一个DO场景:交易主单及子单是是多个模型,考虑到复用表结构,reposity在实现时会将主单与子单合统一转成一个交易表DO进行存储;
一个模型Entity对应多个DO场景:商品表考虑到性能会有商品主表及商品扩展表,在模型上商品是一个Entity,但在持久化时会拆成商品主表DO及商品扩展表DO进行持久化;
Reposity注意事项
标准的reposity接口有:save,query方法;其中save入参是领域模型Entity,如交易单存储入参是OrderModel,OrderModel包含了主单及子单;save方法通过聚合根entity是否有id来判断是插入还是更新;
但有时聚合根entity中有部分实体是没变化的,这个不需要进行更新,这个解决方案有变更追踪方案;
变更追踪原理:将入参的entity与数据库查询出来的entity进行比较(所有字段值)如果有变化才做更新;变更追踪方案有一定的复杂度(复杂度上来容易出问题),所以在实现时个人更倾向于由域服务决定要更新什么;
比如交易支付场景对外提供交易支付接口,入参是:交易主单+交易子单;域服务这层针对入参子单进行处理(对子单调用支付域进行支付),最新调用reposity save时传入当前主单及当前支付的交易子单(而不是全部的交易子单)进行子单状态更新;
总结
本章介绍了DDD战术设计,描述了解决方案模型落地&分层架构选型&Reposity实现;
其中Reposity实现部分大的分层依赖是合理的,对于提升更新性能引入变更追踪导致的复杂度需要辩证去看;