**贫血模型:**数据和业务逻辑被分隔到不同的类中。数据与操作分离,破坏了面向对象的封装特性,是典型的面向过程的编程风格。
**充血模型:**数据和对应的业务逻辑被封装到同一个类(领域模型)中。满足面向对象的封装特性,是典型的面向对象编程风格。
**领域驱动设计(Domain Driven Design - DDD):**一种设计思想,主要是用来指导如何解耦业务系统,划分业务模块,定义业务领域模型及其交互。微服务就是一种典型的实践。

为什么DDD使用的是充血模型
https://www.cnblogs.com/skystarry/p/14948268.html

实体和值对象

实体和值是组成领域对象的基本单位。
实体的表现形式是实体类,这个类包含了实体的属性和方法,通过这些方法实现实体自身的业务逻辑。领域模型中的实体是多个属性、操作或行为的载体。实体以 DO(领域对象)的形式存在,每个实体对象都有唯一的 ID。可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。但是,由于它们拥有相同的 ID,它们依然是同一个实体。在领域模型映射到数据模型时,一个实体可能对应 0 个、1 个或者多个数据库持久化对象。大多数情况下实体与持久化对象是一对一。
值对象是通过对象属性值来识别的对象,它将多个属性值整合成一个概念整体(如:人员信息是个Model,Address是其子属性,Address下可以有其他多个属性值来描述它[省、市、县、街道…])。也就说,值对象描述了领域中的一件东西,这个东西是不可变的,它将不同的相关属性组合成了一个概念整体,本质上就是一个集。(值对象是一些不会修改,只能完整替换的属性值的集合,更关注它的属性和值,它没有太多的业务行为,用于描述实体的一些属性集,被实体引用,依附于实体的值对象基本没有自己的数据库表。
image.png
DDD 引入值对象是希望实现从“数据建模为中心”向“领域建模为中心”转变,减少数据库表的数量和表与表之间复杂的依赖关系,尽可能地简化数据库设计,提升数据库性能。
image.png

聚合和聚合根

聚合

聚合是保证实体和值对象协同工作的组织,它用来确保领域对象在实现共同业务逻辑的同时,保证数据的一致性。
聚合就是由业务和逻辑紧密相关的实体和值对象组合而成,聚合是数据修改和持久化的基本单元,每个聚合对应一个仓储,实现数据的持久化。
聚合在DDD分层架构里属于领域层,领域层包含多个聚合,共同实现核心的业务逻辑。

聚合根

聚合根也称为根实体,它不仅是实体还是聚合的管理者。如果把聚合比做组织,聚合根就是组织的管理者。
聚合根的主要目的是为了避免由于复杂的数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致。

**聚合特点:**高内聚、低耦合,是领域模型中最底层的边界,可以作为拆分微服务的最小单位。如果从业务出发,聚合是可以单独作为一个微服务,以满足版本高频发布和极致弹性的伸缩能力。

**聚合根特点:**聚合根是实体,具有全局唯一标识,有独自的生命周期。一个聚合只有一个聚合根。聚合根内部维护聚合对实体和值对象进行组织管理,通过聚合根调用聚合的直接对象引用来协调调度。聚合根和聚合是通过id关联的方式来协同。

聚合设计原则

  1. 在一致性边界内建立建模真正不变条件
  2. 设计小聚合
  3. 通过唯一标识引用其他聚合
  4. 在边界之外使用最终一致性
  5. 通过应用层实现跨聚合的服务调用

领域事件

领域事件是领域模型中非常重要的一部分,表示领域中发生的事件。一个领域事件将导致进一步的业务操作。实现业务解耦的通知也形成了业务闭环(即一个事件发生后的后续动作)。
识别领域事件就像现实生活中的日常用语“如果发生什么…则…”,“做完…时,请通知”等等。

如何识别领域事件

领域事件通常发生在微服务内的聚合之间或者发生在微服务之间。

  1. 微服务内的领域事件

当领域事件发生在微服务内的聚合之间,领域事件发生后完成事件实体构建和事件数据持久化,事件发布聚合将事件发布到事件总线不关心订阅方的处理结果实现解耦,订阅方接收事件后完成后续处理操作。(注:按照DDD原则,一次事件只能操作更新一个聚合,如果涉及到多个聚合的更新就需要考虑引入事件总线)
微服务内的应用服务,可以通过服务编排和组合方式实现跨聚合访问。对于实时性和一致性要求高的场景,需要引入分布式事务保证发布者和订阅者通过更新数据成功。

  1. 微服务之间的领域事件

跨服务的领域事件会在不同的限定上下文或者领域模型之间实现互相协作,实现服务解耦,减轻服务之间的访问压力。跨服务事件可以推动业务流转或者数据在不同子域和服务间直接流转。
微服务之间的访问可以采用直接访问的方式,实现数据和服务之间实时访问,弊端是跨服务之间需要引入分布式事务,确保数据的一致性。(注:分布式事务会影响系统的性能,增加服务之间的耦合,尽量避免使用分布式事务

下图为领域事件设计驱动业务流程
image.png
领域事件驱动是异步机制,来实现服务之间的解耦

领域事件总体架构

领域事件处理包括:事件构建和发布、事件数据持久化、事件总线、消息中间件、事件接收处理等。
image.png

1. 事件构建和发布

事件属性包括:事件唯一标识、发生时间、事件类型、事件源。**事件唯一标识是全局唯一的,以便能在多个限定上下文中传递。**事件基本属性是记录事件发生的背景。
事件中还需要包含业务属性,记录那一刻发生的业务数据,这些数据会传给订阅者进行下一步的业务操作。
所以,事件实体是由事件属性和业务属性一起构成的。事件实体依赖于聚合根,领域事件发生后其中的业务数据不能再进行修改操作。
image.png
注:事件发布之前需要构建事件实体并持久化。

2. 数据持久化

事件持久化可用于系统之间的数据对账,当服务或者消息中间件挂掉恢复后,能保证流程能正常向后继续执行。(事件数据库和业务数据库不能在一个库中)

3. 事件总线(EventBus)

事件总线是现实微服务内聚合之间领域事件的重要组件,它具有事件分发和接收的作用。
事件总线是进程内的模型,它会在微服务内遍历订阅者列表,采用同步或异步的模型传递数据。
事件分发流程如下:

  - 如果是微服务内的订阅者,则直接发分到指定订阅者
  - 如果是微服务外的订阅者,将事件数据保存到事件数据库,并异步发送到消息中间件。
  - 如果同时存在微服务内和外订阅者,则先分发到内部订阅者,再将事件数据保存在数据库,异步发送到消息中间件。

4. 消息中间件

跨微服务的领域事件大多会用到消息中间件,实现跨服务的分发和订阅。
案例
image.png

DDD分层

DDD分层架构实现各个层级对基础层的解耦,后面在应用层和领域层之间增加上下文环境层。下图展示DDD分层的转变,现在"领域层"是核心层。
image.png
image.png

  1. 用户接口层

用户接口层负责向用户显示信息和解释用户指令,用户可能是:用户、程序、自动化测试、批量处理脚本等等。

  1. 应用层

应用层位于领域层之上,它用来协调领域层内的多个聚合服务和领域对象编排和组合,协作完成业务操作。
应用层也是微服务间的交互通道,完成微服务之间的组合和编排。

  1. 领域层

领域层的作用是用来实现核心的业务逻辑。领域层包括:聚合根、实体、值对象、领域服务等领域模型中的领域对象。
领域模型中的业务主要是由实体和领域服务来实现的,其中实体会用到充血模式来实现所有相关的业务功能。它们的实现业务逻辑不是同级的,当
领域中的某些功能单一的实体对象(或者值对象)不能实现时,领域服务可以组合聚合内的多个实体(或值对象)来实现复杂的业务功能。

  1. 基础层

基础层贯穿了所有层,它的作用是为其他层提供底层技术及基础服务,比如:第三方工具、驱动、消息中间件、网关、文件、缓存及数据库等等。
基础层包括基础服务,采用依赖倒置设计封装了基础资源的服务。实现应用层、领域层、与基础层的解藕,降低了外部资源变化对应用带来的影响。

DDD分层架构原则
每一层只能与位于其下方的层发生耦合。

领域模型中对象的层次从内到外依次是:值对象、实体、聚合和限定上下文。

下图是DDD分层与传统的三层架构的对比,DDD分层将要素重新归类,确定层与层之间的交互规则和职责边界。
image.png
DDD分层架构将业务逻辑层服务拆分到应用层和领域层。应用层来快速响应前端的变化,领域层来实现领域模型的能力。
三层架构采用Dao方式访问数据库,DDD架构采用仓储设计模式,依赖倒置来实现对各个层之间基础资源的解藕。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐