DDD之基本概念
# DDD之基本概念
1.1 微服务简介
(1)单体架构项目
缺点
- 耦合
- 技术栈统一,软件包版本锁定
- 一崩全崩;升级周期长;无法局部扩容
(2)微服务
优点
- 耦合性低,易于开发和维护;可以用不同技术栈;可以单独扩容;互相隔离,影响小;部署周期短
缺点
- 对运维能力要求高;运行效率会降低;技术要求高,需要处理事务最终一致性等问题。
微服务架构应该是进化而来的;微服务的拆分进化。
1.2 DDD简介
DDD(Domain-driven design,领域驱动设计)。
在项目的全生命周期内,所有岗位的人员都基于对业务的相同的理解来开展工作。所有人员站在用户的角度、业务的角度去思考问题,而不是站在技术的角度去思考问题。
1.3 DDD之领域与领域模型
(1)领域
-
“领域”(Domain):一个组织做的事情。子领域。
-
领域的划分:
- 核心域:解决项目的核心问题,和组织业务紧密相关。
- 支撑域:解决项目的非核心问题,则具有组织特性,但不具有通用性。
- 通用域:解决通用问题,没有组织特性。
(2)领域模型
- 对于领域内的对象进行建模,从而抽象出来模型。
- 我们的项目应该开始于创建领域模型,而不是考虑如何设计数据库和编写代码。使用领域模型,我们可以一直用业务语言去描述和构建系统,而不是使用技术人员的语言。
(3)事务脚本
使用技术人员的语言去描述和实现业务事务。没有太多设计,没有考虑可扩展性、可维护性,流水账地编写代码。
1.4 DDD之通用语言、界限上下文
(1)通用语言
一个拥有确切含义的、没有二义性的语言。
(2)界限上下文
通用语言离不开特定的语义环境,只有确定了通用语言所在的边界,才能没有歧义的描述一个业务对象。
1.5 DDD之通用实体、值对象
(1)实体(Entity)
- “标识符”用来唯一定位一个对象,在数据库中我们一般用表的主键来实现“标识符”。主键和标识符的思考角度不同。
- 实体:拥有唯一的标识符,标识符的值不会改变,而对象的其他状态则会经历各种变化。标识符用来跟踪对象状态变化,一个实体的对象无论怎样变化,我们都能通过标识符定位这个对象。
- 实体一般的表现形式就是EF Core中的实体类
(2)值对象(Value Object)
- 值对象:没有标识符的对象,也有多个属性,依附于某个实体对象而存在。
- 定义为值对象和普通属性的区别:体现整体关系。
1.6 DDD之聚合、聚合根
(1)聚合(Aggregate)
-
目的:高内聚,低耦合。有关系的实体紧密协作,而关系很弱的实体被隔离。
-
把关系紧密的实体放到一个聚合中,每个聚合中有一个实体作为聚合根(Aggregate Root),所有对于聚合内对象的访问都通过聚合根来进行,外部对象只能持有对聚合根的引用。
-
聚合根不仅仅是实体,还是所在聚合的管理者
-
整体封装了对部分的操作,部分与整体有相同的生命周期。
-
部分不会单独与外部系统单独交互,与外部系统的交互都由整体来负责。
(2)聚合的划分
- 尽量把聚合设计的小一点,一个聚合只包含一个聚合根实体和密不可分的实体,实体中只包含最小数量的属性
- 小聚合有助于进行微服务的拆分。
1.7 DDD之领域服务与应用服务
(1)概念
- 聚合中的实体中没有业务逻辑代码,只有对象的创建、对象的初始化、状态管理等个体相关的代码。
- 对于聚合内的业务逻辑,我们编写领域服务(Domain Service)。
- 对于跨聚合协作以及聚合与外部系统协作的逻辑,我们编写应用服务(Application Service)。
- 应用服务协调多个领域服务、外部系统来完成一个用例。
(2)DDD典型用例的处理流程
- 第一步,准备业务操作所需要的数据。
- 第二步,执行由一个或者多个领域模型做出的业务操作,这些操作会修改实体的状态,或者生成一些操作结果。
- 第三步,把对实体的改变或者操作结果应用于外部系统。
(3)职责的划分
- 领域模型与外部系统不会发生直接交互,即领域服务不会涉及数据库操作。
- 业务逻辑放入领域服务,而与外部系统的交互由应用服务来负责。
- 领域服务不是必须的,在一些简单的业务处理中(比如增删改查)是没有领域知识(也就是业务逻辑)的,这种情况下应用服务可以完成所有操作,不需要引入领域服务。这样可以避免过度设计。
1.8 DDD之仓储和工作单元
仓储(Repository)和工作单元(Unit Of Work)
- 仓储负责按照要求从数据库中读取数据以及把领域服务修改的数据保存回数据库。
- 聚合内的数据操作是关系非常紧密的,我们要保证事务的强一致性。
- 而聚合间的协作是关系不紧密的,因此我们只要保证事务的最终一致性即可。
- 聚合内的若干相关联的操作组成一个“工作单元”,这些工作单元要么全部成功,要么全部失败。
1.9 DDD之领域事件、集成事件
(1)事务脚本处理“事件”
- 问题1:代码会随着需求的增加而持续膨胀。
- 问题2:代码可扩展性低。“开闭原则”:对扩展开放,对修改关闭。
- 问题3:容错性差。外部系统并不总是稳定的。
解决:采用事件机制
(2)领域事件和集成事件
-
DDD中的事件分为两种类型:领域事件(Domain Events)和集成事件(Integration Events)。
-
领域事件:在同一个微服务内的聚合之间的事件传递。使用进程内的通信机制完成。
-
集成事件:跨微服务的事件传递。使用事件总线(EventBus)实现。
1.10 DDD之贫血模型和充血模型
(1)概念
- 贫血模型:一个类中只有属性或者成员变量,没有方法
- 充血模型:一个类中既有属性、成员变量,也有方法。