DDD应用架构内部分享
Posted 鱼翔空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DDD应用架构内部分享相关的知识,希望对你有一定的参考价值。
上周分享了一篇文章(DDD的应用框架实践分享) 周五又在内部将给大家分享了下。现将分享内容与大家分享。
传统开发的弊病:
-
通过事务脚本模式来开发需求;
-
开发人员热衷于技术并通过技术手段解决问题,而不是深入思考和设计业务的走向;
-
过于重视数据库,围绕数据库和数据模型进行建模,按数据流程进行建模;
-
按技术视角进行业务命名,导致后续迭代以及人员更替时,产品和技术无法对齐;
-
随着业务的发展,到后续业务、技术无法沟通,各种不理解;
-
业务希望技术出排期,技术得撸代码,耗费精力;
-
代码开发的过程中技术和业务耦合,一个场景一个服务,代码流水线;
-
因为技术的问题会导致业务流程的中断,导致异常问题过多;
-
过度方案太多,没有定期消除,导致后续的代码越来越难维护;
-
业务只加需求,不减过期的需求,导致软件越来越臃肿;
DDD解决了什么?
-
通用语言,让开发和业务在语言上统一;
-
战略设计(业务和开发都能看到的软件模型)
-
边界划分(通过限界上下文来划分,在不同的情景下域的作用是不同)
-
领域划分(通过域来确定业务领域,包含核心域、子域、支撑域)
-
分层架构(整洁架构)
-
战术设计
-
聚合(值对象,实体,聚合根)
-
领域事件
最终要达到的目的:
-
业务和技术能够通过领域模型建立通用语言(业务和技术的沟通无障碍);
-
业务边界清晰(微服务的拆分);
-
业务模型独立且模型具备可测试性;
-
技术实现独立,可以随着业务的发展不断的更迭;
名词解析
事务脚本-面向过程
-
事务:执行的业务
-
脚本 一组系统执行的操作,和用户的逻辑关联;
根据接口请求,将业务逻辑通过面向过程组织为解决方案的过程;特点:
-
简单 (仅依赖面向对象语言的少量功能)
-
快速 开发快、上线快
-
扩展性查
-
改动以后测试性差
-
业务复杂后,代码容易变成“大泥球”,系统腐化速度和复杂性呈指数级上升
领域模型
通过面向对象的设计,将业务实现模型化(将类的状态和行为分离);
特点:
-
还原现实世界(复杂)
-
责任清晰
-
边界清晰
-
完全面向对象,状态和行为分离;
-
扩展性强
-
测试性强
-
简单的业务用领域模型反而复杂了;
DDD
是对面向对象设计的改进,开发复杂业务逻辑的一种方式,但不是银弹。
特点:
-
通过领域划分有助于把应用程序分解为服务;
-
每个服务都有自己的领域模型;
-
限界上下文清晰
实体
具有持久化ID的对象,能唯一标识一个条记录的;
特点:
-
标识(identity)唯一不可变,且能区分出具体的事物
-
连续性(continuity),贯穿整个生命周期
-
只有根实体对外暴露(但不排除一些为了冗余而暴露别的信息)
在我们的系统里,用户实体的标识:包含id和租户,只有这两个组合我们才能唯一识别出这个用户
public class CustomerId {
private Long id;
private Integer tenantId;
public CustomerId(Long id, Integer tenantId) {
this.id = id;
this.tenantId = tenantId;
}
public boolean isRealName(){
if (Objects.isNull(this.id) || this.id == 0L){
return false;
}
return true;
}
}
值对象
⽤于描述状态的属性,特征,只关心对象是什么,不关注唯一性。
聚合根
把一组有相同生命周期、在业务上不可分离的实体和值对象放在一起;
领域事件
由特定领域,因触发一个动作而触发的发生在过去的行为事件
特点:
-
动作(一个行为的发生产生的)
-
已发生
工厂
负责聚合根、实体的创建,以及各层之间数据的转化;如:
-
adapter 到领域层的数据转化
-
基础设施层到领域的转换
CQRS
是将 command 与 query 分离的一种模式。CQRS 将系统中的操作分为两类,即「命令」(Command) 与「查询」(Query)。命令则是对会引起数据发生变化操作的总称,即我们常说的新增,更新,删除这些操作,都是命令。而查询则和字面意思一样,即不会对数据产生变化的操作,只是按照某些条件查找数据。
CQRS 的核心思想是将这两类不同的操作进行分离,然后在两个独立的「服务」中实现。这里的「服务」一般是指两个独立部署的应用。
架构
DDD分层架构
DDD分层架构中有很重要的依赖原则:每层只能与位于下方的层发生耦合,类似于网络的7层或TCP/IP的4层模型架构,每一层各司其职,并且只关心向下一层的实现,而不会出现各层耦合。
洋葱架构
同心圆代表软件的不同部分,从里向外依次是领域模型,领域服务,应用服务和外层的基础设施和用户终端。洋葱架构根据依赖原则,定义了各层的依赖关系,越往里依赖程度越低,代码级别越高,越是核心能力。外圆代码依赖只能指向内圆,内圆不需要知道外圆的情况,
六边形架构
将应用分为内六边形和外六边形两层,内六边形实现应用的核心业务逻辑。外六边形完成外部应用,基础资源等的交互和访问,对于与不同的外部系统交互,由外六边形的适配器负责协议转换,保证内六边形业务逻辑的干净。
四种领域模型
-
失血模型:领域对象只包含setter和getter方法,业务逻辑放到service
-
贫血模型:包含属性的getter/stter和所有业务逻辑;
-
充血模型:包含属性的getter/stter和所有业务逻辑,再加上与数据库的操作。这是DDD提倡的模型
-
胀⾎模型,删除Service层,所有逻辑都放到模型中,模型直接对接web层
通过什么手段来落地?
-
事件风暴
-
用例+用户故事
我们是怎么做的?
-
使用半贫血模型;领域实体里包getter和setter,包含部分共用的业务逻辑,但不包含所有的业务逻辑;
-
复杂业务逻辑通过service构建的领域服务来处理;
-
domain作为底层,依赖倒置基础设施层;
-
domain层通过gateway,做了一层防腐,gateway就是一组接口,具体实现根据实际情况选择,db、缓存、rpc、组合等;
-
复杂业务使用领域服务构建,简单业务直接应用层调用基础设施层;
分享后的大家问的几个问题
-
事务如何添加?
-
如果整个领域服务是一个事务,则在application层进行加事务,在application里需要注意,不要破坏领域;
-
如果只是其中领域服务的一个小方法,可以在gateway的实现里实现;
-
性能问题如何解决?性能属于技术问题,和业务无关;
-
比如常见的CQRS,将写和读分离,写异步
-
先写redis再写数据库
-
外部调用短连接变长链接;
-
事实数据做宽表;
-
事件驱动
-
采用哪种架构(六边形、洋葱架构)
-
最核心的整洁架构+DDD的分层架构,并没有特别严格;
-
最核心的是业务逻辑和与模型;
-
随着业务的发展与技术发展不断的适配;
参考:
https://blog.csdn.net/significantfrank/article/details/110934799
https://www.cnblogs.com/jiyukai/p/14830869.html
https://zhuanlan.zhihu.com/p/115685384
如果觉得对你有帮助,请关注公众号:5ycode,后续会不断更新哦
以上是关于DDD应用架构内部分享的主要内容,如果未能解决你的问题,请参考以下文章