DDD领域驱动设计
DDD笔记
限界上下文:限界上下文是一个显示的概念,领域模型存在于限界上下文中,并且每个领域模型在边界之内都有特殊含义; 一个限界上下文需要很好的表达一个概念;模型不算重要,因为同个模型可能在不同上下文中表达的意思已经完全不同,跟我们中国人说话一样,语境很重要;所以限界上下文才是王道;
隔离内核:先将包隔离开;
限界上下文去思考全局的模型归属
上下文的边界怎么处理数据
六边形架构:端口-适配器架构,每有一个新的端口(不同方式的请求)都会新建一个对应的适配器
有的适配器处理输入,有的处理输出
REST 架构: 资源展示状态转移
Rest 与 DDD :通常情况下 需要为接口单独创建一个对象 与领域对象区分开,目的是为了让核心领域与系统接口接解耦
命令和查询职责分离:CQRS
命令模型:repostory查询只有findById接口
查询模式与命令模式 采用不同数据源,查询模型监听命令模型发出的事件;
CQRS面临的问题:命令模型与查询模式 数据一致性
Dp:domain Primitive
好处:将隐性概念显性化,也就是将无状态值封装成对象概念;
将隐性的上下文显性化,有些概念是隐含在具体的上下文中,需要将此概念显现提出来,封装成对象
封装多对象行为,将多个对象进行的一系列操作,封装成一个对象概念中的某个行为;
domain primitive(领域 基本) :
domain primitive 是在一个特定领域里,拥有精准概念定位,拥有自我校验能力,
- 实体
值对象可代替基础类型唯一标识
值对象存放唯一标识表达的概念更清晰,并且还可以具有自己的行为;
委派标识:委派标识是为了迎合使用ORM框架而创建的,比如没有任何业务含义的主键Id
领域标识:领域标识是领域内本身就具有唯一标识能力的单个或一组属性;
当存在委派标识时,就不需要领域标识作为主键,直接委派就好;
比如Hub对象,委派标识是Id,领域标识是name
真正的领域模式:
DDD 阿里:https://zhuanlan.zhihu.com/p/343388831
Domain 转换成 DO 应该写在基础设施层
值对象:
要经常使用值对象,特别是存在上下游的关系中,下游直接使用值对象,不需要完全跟上游对象一样,创建一个新的值对象,只关心自己需要的属性,最小化集成;
用值对象表示标准类型;
什么是标准类型,例如phoneNumber电话,这个电话可以是家庭电话,工作电话,移动电话,那么此时可以使用值对象来表示标准类型。在java中,枚举类型就是实现标准类型的简单方法;
特性:
1.概念整体 2.不变性 3.可替换性 4.无副作用行为 5.下游最小集成 6.表示标准类型
将值对象的setter方法设置为私有,是保持值对象不变性的重要手段;
领域事件:
领域事件是对象,命名规则根据某个行为命名,例如待定项提交完毕后就是对象名称;
设计聚合原则:
聚合是用来封装真正的不变性;
聚合间的引用:通过唯一标识引用其他聚合;
边界之外的使用最终一致性;
创建具有唯一标识的根实体(根实体:只有最小数量的属性或值对象类型属性),也可以叫做设计小聚合
聚合内部优先使用值对象;
聚合是用来封装真正的不变性;换个说法;
聚合最基本的原则是:在一致边界之内建模真正的不变条件;
一致边界可以理解为描述封装的是同一个业务概念;
不变条件指的是,必须遵守的业务规则;
真正的不变条件可以大概分为两点,数据的一致性和事物的一致性;
数据的一致性:比如有函数是A+B= C,当A=1,B=2,C一定等于3,那么聚合根就是用来约束此业务规则的,保障此业务规则的不变性,不能被其他的地方所改动,通常业务逻辑运行完都需要存储到数据库,那么此时就会引入事物的一致性;
事物一致性可分为强一致性和最终一致性,聚合内要保证强一致性,聚合之间保障最终一致性;
如果某个聚合中的对象,可以脱离聚合单独存在与上下文中,那么此时该对象可以从聚合中删除;
聚合是领域对象的显示分组
聚合间的引用:通过唯一标识引用其他聚合:
通常来说聚合如果直接引入聚合,但是聚合是可以单独存在的,那么事务操作就需要两次,已经不满足一致边界之内的不变条件了;
边界之外的使用最终一致性:
边界之外的聚合,直接使用领域事件,实现最终一致性
工厂:
工厂只帮助创建复杂的聚合对象,不做其他业务逻辑
聚合工厂方法:得到本身属于聚合中的其他对象,保持一致性;
使用工厂方法能够更生动的知道对象从何而来,更符合通用语言;
也更符合保障聚合边界内的一致性原则,不能单独获取聚合里的对象,聚合的对象都必须从聚合中得到;
领域服务工厂方法:帮助构造独立的对象,因为该对象是独立的,没有聚合可去,所以此时需要用领域服务来表达通用语言;
Repository资源库: 安全的存储区域,来用管理资源
严格来说只有聚合才应该有资源库;
面向集合的资源库:客户端显示调用add()方法
面向持久化的资源库:客户端显示调用save()方法
在客户端的角度add方法更像是在操作集合,save方法像是在做持久化
repository的接口需要与聚合放在同一个包中,但是具体的实现放入基础设施层
Domain 依赖与抽象的吗model层的接口,具体实现也依赖于model层的接口;
资源库与数据访问对象DAO的区别: 看待问题的角度不同
集成限界上下文: 教你限界上下文之间该如何去处理
防腐层:
CollaboratorService协作服务 负责封装如何获取到对应值对象的概念;
CollaboratorService 调用 -》UserRoleAdapter用户角色适配器
UserRoleAdapter 适配器 fegin 远程服务得到response,然后将response作为入参,放入CollaboratorTranslator 协作值对象翻译器中,得到具体的值对象;