我是靠谱客的博主 忧伤斑马,最近开发中收集的这篇文章主要介绍领域驱动设计1. 领域2. 限界上下文3. 领域对象4. 聚合5. 领域事件6. DDD分层架构,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
1. 领域
1.1 领域的定义
- 领域用来确定业务的边界与范围。是这个边界和范围内要解决的业务问题域。
1.2 领域的划分
- 核心域
- 决定产品和公司核心竞争力的子域。如腾讯的社交。阿里的电商。
- 支撑域
- 用于支持和协作建立核心域的其他子域。如很多公司的支付业务。
- 通用域
- 一般已有开源解决方案,不能直接给公司带来价值但又不可或缺。如认证系统,日志管理。
1.3 为何需要划分领域
- 公司的资源是有限的,为了最优分配资源,实现利益最大化。
- 优先做好核心域。还有余力可以将支撑域也做成核心域。等到公司体力做够大了再精力投入通用域。
2. 限界上下文
2.1 限界上下文的定义
- 限界上下文定义了领域的边界
- 在同一界限上下文中,我们可以使用统一的语言进行交互。
- 用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等有一个确切的含义,没有二义性。
我们将限界上下文内的领域模型映射到微服务,就完成了从问题域到软件的解决方案
- 一个限界上下文理论上就可以设计为一个微服务。
2.2 如何划分限界上下文
- 首先进行事件风暴。
- 然后提取领域对象。
- 最后对领域对象聚合并分组,通过业务的内聚性和关联度划分边界,结合限界上下文的定义进行判断,并给出上下文名称。
核心是将大的领域分解成高内聚低耦合的子域
3. 领域对象
3.1 什么是领域对象
- 领域模型由领域对象组成。
- 领域对象主要分为实体对象和值对象。
3.2 什么是实体
- 实体
对象
拥有唯一标识符,该对象在经历各种状态变更仍能保证标识符一致。 - 实体对象强调唯一标识。如商品是商品上下文的一个实体,通过商品的唯一ID标识一个商品,不管商品其他数据如何变化,只要这个ID不变,他始终代表同一个商品。
在领域驱动设计中,实体类通常采用充血模型。
3.3 实体的数据库形态
DDD设计中是现有领域模型,再针对业务场景构建实体对象,最后将实体对象映射到数据持久化对象
- 所以,实体对象和数据库持久化对象之间不一定是一一对应。可能是一对一,多对一,一对多,以及一对零。
- 如权限实体对应user和role两个持久化对象
- 如为了减少链表查询,将客户和账户两个实体映射为一个持久化对象。
3.3 什么是值对象
- 没有标识符的对象叫做值对象。
- 值对象时若干相互关联的属性集合。
- 如
省、市、县和街道等属性
构成了地址
这个值类型。该类的一个具体实例就是一个值对象。
- 如
- 实体类会包含业务逻辑,包含修改数据的行为。而值类型则基本不包含业务逻辑,很少涉及修改数据的行为。
- 值类型一般是实体类型的一部分,用于描述实体的特征。
// 实体类
public class Person {
public String ID; // 实体类的标识字段
public String name;
public int age;
public boolean gender;
public Address address; // 值类型作为实体类的属性。
// 各种实体方法,省略
}
public class Address {
public String province;
public String city;
public String country;
public String streat;
// 值对象方法,省略。
}
3.4 值对象的两种运行形态
属性嵌入
// 实体类
public class Person {
public String ID; // 实体类的标识字段
public String name;
public int age;
public boolean gender;
public Address address; // 值类型作为实体类的属性。
// 各种实体方法,省略
}
public class Address {
public String province;
public String city;
public String country;
public String streat;
// 值对象方法,省略。
}
序列化大对象
// 实体类
public class Person {
public String ID; // 实体类的标识字段
public String name;
public int age;
public boolean gender;
public String address; // json类型,将Address序列化后嵌入实体中。
// 各种实体方法,省略
}
public class Address {
public String province;
public String city;
public String country;
public String streat;
// 值对象方法,省略。
}
3.5 值对象如何持久化
- 在DDD中,我们将值对象嵌入到实体对象对应的数据库表中。
- 实体对象通过序列化大对象将值对象嵌入,简化了数据库的设计。
- 总结:
在领域建模时,我们将部分对象设计为值对象,保留对象的业务含义,同时又减少了实体的数量;在数据建模时,我们可以将值对象嵌入实体,减少实体表的数量,简化数据库设计
3.6 值对象的优缺点
- 优点
- 简化数据库设计,减少实体表的数量。
- 缺点
- 无法满足基于值对象的快速查询。
- 值对象缺乏概念完整性。(一般值对象作为实体表中的一个json字段)
4. 聚合
4.1 聚合的定义
- 一个限界上下文中会有多种不同类型的实体。我们会按照高内聚低耦合的要求,将这些不同的实体进行聚合。
- 每一个聚合里面一定要包含一个聚合根,以及它的上下文边界。
- 70%的场景下,一个聚合内都只有一个实体,那就是聚合根。
- 一个限界上下文可能包含多个聚合,但一个聚合只能存在于一个限界上下文
同一个聚合下的实体通过领域服务进行交互,不同聚合下的实体交互通过应用服务进行交互
4.2 聚合根
- 大多数情况下一个聚合里面只有一个实体,那就是聚合根。
- 聚合内
聚合根对实体和值对象采用直接对象引用
的方式进行组织和协调。(所以当我们拿到聚合根实体后,那么该聚合下的所有实体和值对象也可以得到) - 聚合之间是
通过关联外部聚合根ID的方式
引用,而不是直接对象引用的方式。 - 如果想要访问另一个聚合下 的实体,只能通过聚合根来访问。
- 如:
帖子--回复
:帖子是一个聚合根,回复是一个聚合根,回复只聚合帖子的唯一标识用户--消息
:用户是一个聚合根,消息是一个聚合根,消息值聚合根用户的唯一标识订单--订单子项
:订单是一个聚合根,订单需要聚合订单子项,订单子项不是聚合根,它需要依托订单存在商品--收货地址
:商品是一个聚合根,收货地址是一个聚合根,他俩之间无直接依赖关系。所以不需要聚合对方的唯一标识。
领域层包的划分规则通常为
----限界上下文
–domain
------聚合A
-------- (聚合根、实体、值对象、领域服务、资源库、领域事件)
------聚合B
-------- (聚合根、实体、值对象、领域服务、资源库、领域事件)
- 各个职责
- 资源库:提供聚合根或者持久化聚合根
- 聚合根:负责封装实现业务逻辑
- 领域服务:负责封装实现业务逻辑(
对于不能直接通过聚合根完成的业务操作就需要通过领域服务
) - 应用服务:不处理业务逻辑,只是对领域服务/聚合根方法调用的封装。
4.3 一次业务处理的经过
应用服务 ->领域服务 ->通过资源库获取聚合根
->通过资源库持久化聚合根
->发布领域事件
应用服务->通过资源库获取聚合根
->通过资源库持久化聚合根
->发布领域事件
4.4 调用原则
- 聚合根不能直接操作其他聚合根,聚合根之间只能通过聚合根ID引用
同限界上下文内的聚合之间的领域服务可直接调用
。- 两个限界上下文的交互必须通过应用服务层抽离接口(适配层适配)
5. 领域事件
5.1 领域事件驱动设计
- 领域事件驱动设计可以切断领域模型之间的强依赖关系,事件发布完成后,发布方不必关心后续订阅方事件处理是否成功,实现了领域模型的接口。
- 在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一致性,而是基于事件的最终一致性。
- 领域事件可以发生在微服务内的聚合之间,但是绝大多数是发生在微服务之间。
事件驱动本质是将同步调用转为异步调用,由强一致性转为最终一致性
5.2 微服务内的领域事件
- 用的不多,同一个微服务进程内,编程语言层面上几乎可以很好地进行业务编排交互。
5.3 微服务之间的领域事件
- 跨微服务的事件机制要考虑
事件构建,发布与订阅,事件数据持久化,消息中间件以及分布式事务机制
如果微服务之间不使用领域事件,则微服务之间的访问也可以采用应用服务直接调用的方式,但是这个需要引用分布式事务机制,以确保数据的强一致性。
5.4 事件的基本属性与业务属性
基本属性
- 事件唯一标识
- 发生时间
- 事件类型
- 事件源
业务属性
- 记录是按发生那一刻的业务数据。会随事件传输到订阅方
5.5 事件构建与发布
- 事件基本属性和业务属性一起构成了
事件实体
,事件实体依赖聚合根。 - 领域事件发生后,事件中的业务数据不再修改,因此业务数据往往以序列化值对象的形式保存。这种存储格式在消息中间件中也比较容易解析和获取。
public class DomainEvent {
public String ID;
public long timeStamp;
public String source;
public String type;
public String data;
// 事件的各个方法,省略
}
5.6 事件处理流程
- 事件构建
- 事件发布
- 事件持久化
- 事件接收
- 事件处理
6. DDD分层架构
6.1 用户接口层
- 负责向用户显示信息和解释用户指令。
6.2 应用层
- 应用层是很薄的一层,理论上不应该有业务规则和逻辑。
- 应用层位于领域层之上,因为领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象完成服务编码和组合,协助完成业务操作。
此外应用层也是微服务之间交互的通道,它可以调用其他微服务的应用服务,完成微服务之间的服务组合和编排。
应用服务应该在应用层,他负责服务的组合,编排和转发,负责处理业务用例的执行顺序以及结果的拼装,以及粗粒度的服务通过API网关向前端发布。
- 应用服务还可以进行安全验证,权限校验,事务控制,发送和订阅领域事件等。
6.3 领域层
- 实现企业核心业务逻辑。
- 领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象
- 领域中的某些功能单一实体不能实现时,领域服务就会出马,它可以组合聚合内的多个实体,实现复杂的业务逻辑。
6.4 基础层
- 基础层贯穿所有层,它的作用是为其他各层提供通用的技术和基础服务。
- 如第三方工具,驱动,消息中间件,网关,文件,缓存以及数据库,还有数据持久化。
6.5 分层架构的原则
- 每层只能与位于其下方的层发生耦合
最后
以上就是忧伤斑马为你收集整理的领域驱动设计1. 领域2. 限界上下文3. 领域对象4. 聚合5. 领域事件6. DDD分层架构的全部内容,希望文章能够帮你解决领域驱动设计1. 领域2. 限界上下文3. 领域对象4. 聚合5. 领域事件6. DDD分层架构所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复