DDD领域驱动设计
- 早期设计流程图
- 面向对象的系统设计流程
DDD落地到数据库
- 领域对象持久化思想
- 将暂时不使用的对象从内存中持久化存储到磁盘中,再次使用这个领域对象时,根据key值到数据库中查找这条记录,然后将其恢复成领域对象,应用程序就可以继续使用它
- DDD的数据库设计变成了以领域模型为核心,如何将领域模型转换成数据库设计的过程
传统的4中关系直接转换
继承关系的3种设计方案
-
- 优点:简单,整个继承关系的数据全部保存在这个表
- 缺点:造成”表稀疏“
转换成NoSQL数据
- 关系型数据库,遵循第三范式,使得数据库能够大幅度降低冗余,但是数据库查询需要频繁使用
join
操作,在高并发场景性能低下 - NoSQL数据库,将
join
的查询在写入数据库表前先进行join操作,直接写到一张表单中进行分布式存储,这张表称为“宽表”
领域模型指导程序设计
服务(Service)
- 标识的是在领域对象之外的操作与行为,接收用户的请求和执行某些操作
- 当用户在系统界面中进行操作时,会向系统发送请求,“服务”去接收用户的这些请求,然后根据需求去执行相应的方法,所有操作都完成后,再将实体或者值对象中的数据持久化到数据库
实体(Entity)
- 通过一个唯一标识字段来区分真实世界中的每一个个体的领域对象
- 可变性是实体的特点
值对象
- 代表的是真实世界中那些一成不变的、本质性的事物
- 不变性是值对象的本质
贫血模型与充血模型
-
贫血模型
- 有很多POJO对象,除了一堆get/set方法,几乎没有任何业务逻辑
- 相比充血模型,更加简单易行
-
充血模型
- 将领域模型中的原貌直接转换为程序中的领域设计,各种服务操作不在服务中实现,而是在领域对象中实现
- 保持了领域模型的原貌,代码修改起来比较直接
- 保持了对象的封装,已于变更
-
贫血模型比充血模型更加简单易行
-
充血模型需要具备更强的设计与协作能力
- 充血模型需要开发人员具有更强的OOA/D能力,分析业务、业务建模与设计能力
- 充血模型需要有较强的团队协作能力
- 贫血模型所有业务处理过程都交给Service去完成
-
贫血模型更容易应对复杂的业务场景
- 采用贫血模型,将复杂的业务处理场景,划分成多个独立的步骤然后将这些独立的步骤分配给多个Service串联起来执行
-
将需要的业务逻辑放到领域对象中,按照充血模型去设计
- 在领域模型中出现类似继承、多态的情况
- 软件设计的过程中将一些类型或者编码进行转换
- 在软件设计中更好的表现领域对象之间的关系
- “聚合”,在真实世界中代表整体与部分的事务
-
除此之外的其他业务逻辑放到Service中,按照贫血模型设计
聚合、仓库与工厂
聚合的设计
- 聚合表达的是真实世界中整体与部分的关系
- 创建订单时:将订单明细创建在订单中
- 保存订单时:同时保存订单表与订单明细表,并放在同一个事务中
- 查询订单时:同时查询订单表与订单明细表,并将其装配成一个订单对象
- 整体不存在时,部分就变的没有了意义
- 部分被封装在整体中,通过整体去操作部分,整体就是外部访问的唯一入口:聚合根
- 增删改的业务可以采用领域驱动设计
聚合的实现
-
仓库(Repository)
-
系统将领域对象放在仓库,需要时通过ID到仓库中取获取;
-
通过ID获取领域对象
- 仓库通过ID先到缓存中进行查找,找到直接返回
- 没找到,通知工厂,工厂调用DAO去数据库查询,然后装配成领域对象返回给仓库
- 缓存对象,返回对象
-
与数据访问层(DAO)区别
扫描二维码关注公众号,回复: 16244913 查看本文章- 当数据保存时,由DAO负责保存,但保存的是某个单表
- 当数据查询时,通过DAO查询,查询的是某个单表
-
-
DDD 的工厂
-
系统要通过ID装载一个订单
- 订单仓库将任务交给订单工厂,订单工厂分别调用订单DAO、订单明细DAO和用户DAO去进行查询
- 将订单明细对象与用户对象,分别set到订单对象的“订单明细”与“用户”属性中
- 订单工厂将装配好的订单对象返回给订单仓库
-
通过仓库与工厂,对原有的DAO进行一层封装在保存、装载、查询等操作中,加入聚合、装配等操作将这些操作封装起来,对上层程序屏蔽
限界上下文
将整个系统划分成许多相对独立的业务场景,在一个一个业务场景中进行领域分析与建模这样的业务场景称之为**“问题子域”**,简称“子域”。
领域驱动核心的设计思想-------将对软件的分析与设计还原到真实世界中,真实世界的业务与问题叫做“问题域”,业务规则与知识叫“业务领域知识”。
限界上下文:一个复杂系统的领域驱动设计,是以子域为中心进行领域建模,绘制出一张一张的领域模型设计
上下文地图:限界上下文之间的相互关系
- 限界上下文内的高内聚
- 每个限界上下文内的实现的功能都是软件变化的同一个原因的代码
- 限界上下文间的低耦合
- 限界上下文通过上下文地图互相调用时,通过接口进行调用
微服务
微服务最佳实践DDD
- 微服务设计最核心的难题是微服务的拆分,要讲究小而专的设计,要低耦合、高内聚
- 领域驱动设计解决微服务如何拆分,实现微服务的高内聚与单一职责的问题
微服务最佳的实践是DDD
- 从DDD开始需求分析、领域建模,逐渐建立起多个问题子域
- 将问题子域落实到限界上下文,它们之间的关联形成上下文地图
- 各子域落实到微服务中贫血模型或者充血模型的设计,从而在微服务之间依据上下文地图形成接口
微服务设计过程
- 统一语言建模(解决需求分析困境)
- 了解业务,从客户那里去学习,学习业务专业术语
- 事件风暴会议
- 在产品经理的引导下,与业务专家开始梳理当前的业务中有哪些领域事件
- 领域事件:已经发生,需要保存的操作
- 增删改,查询不使用DDD,使用其他的需求分析
- 针对每一个领域事件,围绕它进行业务分析,增加各种命令与事件,进而思考与之相关的资源、外部系统与时间
- 识别模型中可能涉及的聚合及其聚合根
- 在产品经理的引导下,与业务专家开始梳理当前的业务中有哪些领域事件
微服务落地
整洁架构
在DDD实现过程中,技术中台应当能够封装那些繁琐的聚合操作仓库与工厂的设计,以及相关的各种技术
中台
将以往业务系统中可以复用的前台与后台代码剥离个性、提取共性,形成的共用组件。
- 业务中台:将抽象的业务组件,做成微服务各个业务系统都可以使用
- 技术中台:封装各个业务系统所要采用的技术框架,设计出统一的API
- 数据中台:整理各个业务系统的数据,建立数据存储与运算的平台
大前端 + 技术中台
- 降低业务开发的工作量,提高开发速度,降低技术门槛
- 命令与查询职责分离模式 CQRS
- 命令(增删改操作);采用领域驱动设计思想进行软件设计
- 查询操作;采用事务脚本模式,即直接通过SQL语句进行查询
增删改的架构设计
-
单 Controller 的设计
- 如果调用的方法有值对象,必须将值对象放在方法的第一个参数上
- 如果要调用的方法既有值对象,又有其他参数,则值对象中的属性与其他参数放在该 Json 对象中
- 在实际项目中需要在 Controller 前进行权限校验,来规范前端可以调用的方法;使用服务网关或者 filter 进行校验
- Controller 的流程设计
- 根据前端参数 bean 从Spring 中获取 Service
- 根据前端参数 method,通过反射获取调用方法
- 通过反射获取调用方法的第一个参数作为值对象
- 根据反射获取值对象的所有属性,从前端JSON 中获取对应属性的值,写入值对象
- 根据前端 JSON 获取其他参数
- 将值对象与其他参数,使用反射调用 Service 中的 method 方法
-
单 DAO 的设计
-
值对象中可以有很多属性变量,但只有最终做持久化的属性变量才需要配置
-
每个 Service 在 Spring 中都是统一注入 BasicDao
- 如果使用 DDD 的功能支持,注入通用仓库 Repository
- 如果使用 Redis 缓存,注入 RepositoryWithCache
-
DAO 的设计
-
单 DAO 调用 VObjFactory.getObj(class) 获取配置信息 vObj
-
根据 vObj.getTable() 获取对应的表名
-
for(Property prop: vObj.getPreperties()){
- 通过 prop.getColumn() 获取值对象对应的字段
- 运用反射从值对象中获得所有属性及其对应的值
- 通过以上参数形成 SQL 语句
}
-
通过 SQL 语句执行数据库操作
-
-
基于这个框架进行开发,包括 Spring 的配置、MyBatis 的配置、vObj 的配置建议都采用 XML 文件的形式,而不是采用注解;方便移植
查询功能的架构设计
支持 DDD 的技术中台
传统 DDD 架构
- DDD 的仓库和工厂的设计介于业务领域层与基础设施层之间(接口在业务领域层,实现在基础设施层)
通用仓库与通用工厂设计
- 装载或者查询订单时,要查询订单表,填补与订单相关的订单明细与客户信息、商品信息并装配成一个对象
- 保存订单时,不仅要保存订单表还要保存订单明细表,并将它们放到同一个事务中
- 装饰者模式在原有的功能基础上进行扩展
中…(img-ZI9ldbVe-1678023713068)]
- [外链图片转存中…(img-nAPcOSTD-1678023713069)]
通用仓库与通用工厂设计
[外链图片转存中…(img-PYvF4Kaa-1678023713070)]
- 装载或者查询订单时,要查询订单表,填补与订单相关的订单明细与客户信息、商品信息并装配成一个对象
- 保存订单时,不仅要保存订单表还要保存订单明细表,并将它们放到同一个事务中
- 装饰者模式在原有的功能基础上进行扩展
- [外链图片转存中…(img-RMuT1ZZX-1678023713070)]