DDD领域驱动设计
2021-09-26 12:40:52 10 举报
AI智能生成
DDD领域驱动设计
作者其他创作
大纲/内容
DDD是什么
DDD是一种设计思想,从业务领域视角划分领域边界, 使用通用语言进行高效沟通, 抽象出业务领域模型,解决微服务设计过程中, 边界难以划定的难题
DDD的核心概念
领域
就是这个边界内要解决的业务问题域
子域
核心域
决定产品和公司核心竞争力的子域是核心域
通用域
通用系统,没有企业特点限制如:【认证、权限等】
支撑域
具有企业特性, 具备"定制开发"的特性【消息平台,数据字典平台等】
通用语言
在限界上下文内保证语义的唯一含义,通用性,一致性
限界上下文
领域可以被拆分成多个子领域, 一个领域相当于一个问题域, 拆分的过程就是大问题拆分为小问题的过程. 每个领域模型都有他对应的限界上下文, 理论上限界上下文就是微服务的边界
实体
领域模型中的实体是多个属性, 操作和行为的载体, 拥有唯一标志. 实体和值对象是组成领域模型的基础单元, DDD中采用充血模型构建实体. 使用ID即可判定两实体是否相等
值对象
含义
多个相关联属性,放到一个字段中去表示,值对象也就相当于实体的一个属性
代码形态
如果值对象是单一属性,则直接定义为实体类的属性;这种情况属性和值对象的定义就比较模糊了
如果值对象是属性集合,则把它设计为 Class 类,Class 将具有整体概念的多个属性归集到属性集合,这样的值对象没有 ID,会被实体整体引用
运行形态
分支主题
属性嵌入的方式
序列化的方式
分支主题
数据库形态
DDD 引入值对象是希望实现从“数据建模为中心”向“领域建模为中心”转变,减少数据库表的数量和表与表之间复杂的依赖关系;尽可能地简化数据库设计,提升数据库性能
在领域建模时,我们可以将部分对象设计为值对象,保留对象的业务涵义,同时又减少了实体的数量;在数据建模时,我们可以将值对象嵌入实体,减少实体表的数量,简化数据库设计
值对象的特点
无 ID,不可变,无生命周期,用完即扔。值对象之间通过属性值判断相等
聚合
领域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性
聚合在 DDD 分层架构里属于领域层,领域层包含了多个聚合,共同实现核心业务逻辑。
一个微服务可以包含多个聚合,聚合之间的边界是微服务内天然的逻辑边界。有了这个逻辑边界,在微服务架构演进时就可以以聚合为单位进行拆分和组合了,微服务的架构演进也就不再是一件难事了
聚合根
如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者
首先它作为实体本身,拥有实体的属性和业务行为,实现自身的业务逻辑。
其次它作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。
最后在聚合之间,它还是聚合对外的接口人,以聚合根 ID 关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。也就是说,聚合之间通过聚合根 ID 关联引用,如果需要访问其它聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体
聚合根是实体,有实体的特点,具有全局唯一标识,有独立的生命周期。一个聚合只有一个聚合根,聚合根在聚合内对实体和值对象采用直接对象引用的方式进行组织和协调,聚合根与聚合根之间通过 ID 关联的方式实现聚合之间的协同
领域事件
场景
发生在微服务内的聚合之间
如果数据的实时性、一致性要求较高
微服务内应用服务,可以在应用层通过跨聚合的服务编排和组合,以服务调用的方式完成跨聚合的访问
如果数据的实时性、一致性要求较不高
通过event事件完成
发生在微服务之间
如果数据的实时性、一致性要求较高
直接调用的方式完成
如果数据的实时性、一致性要求较不高
消息中间件:MQ
事件风暴
梳理用户旅程, 分析业务场景, 领域专家和开发人员头脑风暴的过程, 进而确定 限界上下文, 通用语言,找出实体, 值对象等 依次 进行领域分解, 建立领域模型
DDD分层架构
分支主题
用户接口层
负责向用户显示信息和接受用户的指令, 包括:用户, 程序, 自动化测试, 批处理等.
应用层
应用层是很薄的一层,理论上不应该有业务规则或逻辑,主要面向用例和流程相关的操作
但应用层又位于领域层之上,因为领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象完成服务编排和组合,协作完成业务操作
应用层也是微服务之间交互的通道,它可以调用其它微服务的应用服务,完成微服务之间的服务组合和编排
领域层
领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则
领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象
领域服务可以组合聚合内的多个实体(或者值对象),实现复杂的业务逻辑。
基础层
基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化
基础层包含基础服务,它采用依赖倒置设计,封装基础资源服务,实现应用层、领域层与基础层的解耦,降低外部资源变化对应用的影响
分层原则
每层只能与位于其下方的层发生耦合
分层架构方式
严格的分层架构
(建议严格采用)任何层只能对位于其直接下方的层产生依赖。
松散的分层架构
它允许某层与其任意下方的层发生依赖。
合理的架构分层
不要把与领域无关的逻辑放在领域层实现,保证领域层的纯洁和领域逻辑的稳定,避免污染领域模型
不要把领域模型的业务逻辑放在应用层,这样会导致应用层过于庞大,最终领域模型会失焦
如果实在无法避免,我们可以引入防腐层,进行新老系统的适配和转换,过渡期完成后,可以直接将防腐层代码抛弃
DDD微服务模型
分支主题
Interfaces(用户接口层)
它主要存放用户接口层与前端交互、展现数据相关的代码。前端应用通过这一层的接口,向应用服务获取展现所需的数据。这一层主要用来处理用户发送的 Restful 请求,解析用户输入的配置文件,并将数据传递给 Application 层。数据的组装、数据传输格式以及 Facade 接口等代码都会放在这一层目录里。
分支主题
Assembler:实现 DTO 与领域对象之间的相互转换和数据交换。一般来说 Assembler 与DTO 总是一同出现。
Dto:它是数据传输的载体,内部不存在任何业务逻辑,我们可以通过 DTO 把内部的领域对象与外界隔离。
Facade:提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。
Application(应用层)
它主要存放应用层服务组合和编排相关的代码。应用服务向下基于微服务内的领域服务或外部微服务的应用服务完成服务的编排和组合,向上为用户接口层提供各种应用数据展现支持服务。应用服务和事件等代码会放在这一层目录里。
应用层不能请求应用层,如果有这样的需求,可以考虑把代码下移到领域层
分支主题
Event(事件)
publish:主要存放事件发布相关代码
subscribe:主要存放事件订阅相关代码(事件处理相关的核心业务逻辑在领域层实现)
Service(应用服务)
这层的服务是应用服务。应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是一段独立的业务逻辑。你可以将所有应用服务放在一个应用服务类里,也可以把一个应用服务设计为一个应用服务类,以防应用服务类代码量过大。
Domain(领域层)
它主要存放领域层核心业务逻辑相关的代码。领域层可以包含多个聚合代码包,它们共同实现领域模型的核心业务逻辑。聚合以及聚合内的实体、方法、领域服务和事件等代码会放在这一层目录里
分支主题
Aggregate(聚合)
它是聚合软件包的根目录,可以根据实际项目的聚合名称命名,比如权限聚合。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内实现高内聚的业务逻辑,它的代码可以独立拆分为微服务。
Entity(实体)
它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码。实体类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码在领域服务中实现。
Event(事件)
它存放事件实体以及与事件活动相关的业务逻辑代码
Service(领域服务)
它存放领域服务代码。一个领域服务是多个实体组合出来的一段业务逻辑。你可以将聚合内所有领域服务都放在一个领域服务类中,你也可以把每一个领域服务设计为一个类。如果领域服务内的业务逻辑相对复杂,我建议你将一个领域服务设计为一个领域服务类,避免由于所有领域服务代码都放在一个领域服务类中,而出现代码臃肿的问题。领域服务封装多个实体或方法后向上层提供应用服务调用。
Repository(仓储)
它存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个仓储
Infrastructure(基础层)
它主要存放基础资源服务相关的代码,为其它各层提供的通用技术能力、三方软件包、数据库服务、配置和基础资源服务的代码都会放在这一层目录里。
分支主题
Config
主要存放配置相关代码
Util
主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码,你可以为不同的资源类别建立不同的子目录
分支主题
注
DDD与微服务的关系
DDD 主要关注
从业务领域视角划分领域边界,构建通用语言进行高效沟通,通过业务抽象,建立领域模型,维持业务和代码的逻辑一致性
微服务主要关注
运行时的进程间通信、容错和故障隔离,实现去中心化数据管理和去中心化服务治理,关注微服务的独立开发、测试、构建和部署
限界上下文和微服务的关系
理论上限界上下文就是微服务的边界。我们将限界上下文内的领域模型映射到微服务,就完成了从问题域到软件的解决方案
DDD、中台和微服务的关系
中台是抽象出来的业务模型,微服务是业务模型的系统实现,DDD作为方法论可以同时指导中台业务建模和微服务建设,三者相辅相成,完美结合
分支主题
仓储
为了解耦应用逻辑和基础资源,在基础层和上层应用逻辑之间会增加一层,这一层就是仓储层。一个聚合对应一个仓储,仓储实现聚合内数据的持久化。聚合内的应用逻辑通过接口来访问基础资源,仓储实现在基础层实现。这样应用逻辑和基础资源的实现逻辑是分离的。如果变更基础资源组件,只需要替换仓储实现就可以了,不会对应用逻辑产生太大的影响,这样就实现了应用逻辑与基础资源的解耦,也就实现了依赖倒置
找不到聚合根时
关于聚合设计过程中的一些原则问题。大部分的业务场景我们都可以通过事件风暴,找到聚合根,建立聚合,划分限界上下文,建立领域模型。但也有部分场景,比如数据计算、统计以及批处理业务场景,所有的实体都是独立无关联的,找不到聚合根,也无法建立领域模型。但是它们之间的业务关系是非常紧密的,在业务上是高内聚的。我们也可以将这类场景作为一个聚合处理,除了不考虑聚合根的设计方法外,其它诸如 DDD 分层架构相关的设计方法都是可以采用的
领域服务的CRUD是不是都是操作聚合根或整个实体对象,比如我只想根据ID判断记录是否存在,或者返回个别字段,需要返回整个实体对象吗?
其实查询类业务可以不必经过聚合根和仓储。传统方法也可以了。
如果聚合数据比较多,会有延迟加载影响性能。聚合根的主要目的是为了保证数据的一致性,这些场景一般在CU的场景。
事务封装在哪一层?
按照业务逻辑来讲,封装到应用层或领域层都是可以的
领域层调用关系?
分支主题
0 条评论
下一页