用人话说明白DDD(领域驱动设计)

用人话说明白DDD(领域驱动设计)

(〇)微服务设计方法论:DDD

DDD(Domain-Driven Design,领域驱动设计)是一种设计思想,用于指导微服务设计过程中的拆分与边界问题。

微服务应该拆分得多细?各个微服务间的边界如何确定?

DDD强调领域模型和微服务设计的一体性,建立领域模型(Domain Model),再划分微服务边界,而不是脱离领域模型来谈微服务设计。

通俗地来说就是:微服务的划分需要和业务划分相匹配。


(一)核心知识体系

DDD的核心思想:通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型和代码模型的一致性


0.【一个形象的比喻】

初中生物是如何研究一棵树的?我们会将一棵树的组成部分划分为营养器官(根、茎、叶)和生殖器官(花、果实、种子),然后将器官进一步划分为组织,组织又可以进一步划分为细胞,细胞分为细胞壁、细胞膜、细胞质、细胞核……

  • 一棵树就是一个领域,划分出来的根、茎、叶、花、果实、种子就是子域
  • 划分维度到了细胞后就不再划分,细胞之间的细胞壁确定了细胞的边界,这个边界就是限界上下文
  • 细胞中的细胞核、细胞质等一同组成了细胞,它们是一个个的实体,让细胞具有特定功能,所以细胞就是一个聚合,细胞核这个最重要的部分就是聚合根
  • 细胞核具有PH值、大小、重量等属性,这些属性集合描述了细胞核这个实体,它们就是一个值对象

1.【范围】:领域 & 子域

  • 领域:企业的业务范围和在这个范围内进行的活动
  • 领域可以分为多个子域。

比如电商领域分为:商品子域、订单子域、库存子域……


子域类型:核心域、通用域、支撑域

  1. 核心域:决定产品和公司核心竞争力的子域;

找到自己公司业务的核心域,然后投入更多的资源。

  1. 通用域:没有太多个性化诉求(不需要定制、容易买到),同时被多个子域使用的通用功能子域;

比如:认证、权限……

  1. 支撑域:除核心域和通用域之外的其他子域

比如:数据字典……


2.【边界】:限界上下文

  • 限界上下文:不同子域中,不同概念有不同的含义,所以我们在进行领域建模时,必须要有一个明确的领域边界,它是系统内部的一个架构边界,决定了这个系统架构。

限界上下文封装了通用语言,提供了上下文环境,保证了在领域内的术语、业务相关对象都有确切的含义,没有二义性。
比如:商品在销售阶段是商品,但是在运输阶段叫做货物。

  • 系统架构是由系统的内部架构边界以及边界之间的依赖关系决定的,与系统中各个组件之间的通信和调用方式是无关的。所以复杂系统划分的第一步就是划分内部的架构边界,即划分清楚上下文。
  • 领域边界就是通过限界上下文来确定的。

限界上下文本质上也是子域


3.【模型】:实体 & 值对象

  1. 实体(Entity):实体是业务对象,具有业务属性和行为,拥有唯一标识符,且标识符在经历各种状态变更后仍能够保持一致。
  2. 值对象:值对象是属性集合,是对实体状态和特征的描述。值对象具有不可变性(相当于final),更改值对象是用另一个值对象替换。

比如,我们有person这个实体:

idagegenderaddress
120{"address": {"province": "浙江", "city": "杭州"}}

其中的address这个JSON串就是一个值对象。


4.【一致性】聚合 & 聚合根


聚合

  1. 定义:聚合由业务和逻辑紧密关联的实体和值对象组合而成,是数据修改和持久化的基本单元
  2. 作用:让实体和值对象协同工作,保证领域对象在实现共同业务逻辑时的数据一致性
  3. 聚合 = 聚合根 + 上下文边界
  4. 设计原则:一个事务最多只能更改一个聚合的状态,若一次业务操作涉及多个聚合状态的更改,应采用领域事件的最终一致性

聚合根

定义:一组实体和值子对象归属于一个聚合根

即使上下文划分得不太正确,只要有DDD聚合根,未来也可以将聚合根升级为更合适的上下文,甚至拆分成微服务

主要目的:避免复杂数据模型缺少统一的业务规则控制,从而导致实体、聚合之间的数据不一致。

特点:

  • 聚合根用于保证内部实体规则的正确性和数据一致性;
  • 聚合之间通过聚合根ID关联引用
  • 聚合根之间不能共享一个数据库事务,它们之间的数据一致性需要通过最终一致性保障。
  • 聚合根作为一个实体,拥有实体的属性和业务行为;同时聚合根作为聚合管理者,在聚合内部负责协调实体和值对象,使它们按照业务规则完成相同的业务逻辑。

(二)领域事件(Domain Event)

领域事件:发生后通常会导致进一步的业务操作的事件。


1.领域事件分类

(1)微服务内的、聚合间的领域事件

微服务内的领域事件会增加系统的复杂性,建议少用

  • 当领域事件发生在微服务内的聚合之间,领域事件发生后完成事件实体构建事件数据持久化,发布方聚合将事件发布到事件总线,订阅方接收事件数据完成后续业务操作。

事件总线(EventBus)是进程内模型,采用同步或异步模式传递数据。
如果是微服务内的订阅者,直接分发到指定订阅者;如果是微服务外的订阅者,将事件数据保存到事件表中,并异步发送到消息中间件

  • 微服务内大部分事件的集成,都发生在同一个进程内,进程自身可以控制事务,因此不需要引入消息中间件;
  • 如果一个事件需要同时更新多个聚合,根据DDD一个事务只更新一个聚合”的原则,就需要引入消息总线。

(2)跨微服务的领域事件

  • 跨微服务的领域事件会在不同的限界上下文或领域模型之间实现业务协同,其主要目的是实现微服务解耦,减轻微服务之间实时服务访问的压力
  • 跨微服务的事件机制需要总体考虑事件构建、发布和订阅、事件数据持久化、消息中间件,甚至事件数据持久化还需要引入分布式事务机制。

2.领域事件流程

(1)事件的构建

事件 = 事件基本属性 + 业务属性记录事件发生那一刻的业务数据,这些数据会随事件传输到订阅方,以开展下一步的业务操作

事件基本属性至少包括:

  1. 事件唯一标识:应该是分布式全局唯一ID的,以便事件能够无歧义地在多个限界上下文中传递。
  2. 发生时间
  3. 事件类型
  4. 事件源

1
2
3
4
5
DomainEvent<T>:
- id: String
- timestamp: long
- source: String
- data: T

领域事件发生后,事件中的业务数据不再修改,因此业务数据可以以序列化值对象(如JSON)的形式报错,这种存储格式在消息中间件中也比较容易解析和获取


(2)事件的发布

事件发布之前需要先构建事件实体,并持久化
事件发布的方式:

  1. 通过应用服务或者领域服务发布到事件总线/消息中间件
  2. 事件表中利用定时程序或数据库日志捕获计数获取增量事件数据,发布到消息中间件

(3)事件数据持久化

作用:

  1. 事件数据持久化可以用于系统之间的数据对账和审计
  2. 当遇到消息中间件、订阅方系统宕机或者网络中断,在问题解决后仍可以继续后续业务流转,保证数据的一致性

方案:

  1. 持久化到本地业务数据库的事件表中,利用本地事务保证业务和事件数据的一致性;
  2. 持久化到共享的事件数据库中,业务数据库和事件数据库不在一个数据库中,需要通过分布式事务机制保证数据一致性,会对系统性能造成一定影响。

(4)事件接收和处理

微服务订阅方在应用层监听,接收消息中间件中的事件数据,完成事件数据的持久化后,就可以开始进一步的业务处理。


(三)4层架构


1.用户接口层

负责向用户(/程序/自动化测试和批处理脚本)显示信息和解释用户指令。

interfaces

  1. assembler:实现DTO与领域对象之间的相互转化和数据交换。
  2. dto数据传输载体,内部不存在任何业务逻辑
  3. facade提供粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理

2.应用层

应用层理论上不应该有业务规则和逻辑,主要面向用例和流程相关的操作。

作用:

  1. 可以协调多个聚合的服务和领域对象完成服务编排和组合,处理业务用例的执行顺序以及结果拼装,协作完成业务操作。
  2. 应用层也是微服务之间交互的通道,可以调用其它微服务的应用服务,完成微服务之间的服务组合和编排。
  3. 应用服务还可以进行安全认证、权限校验、事务控制、发送和订阅领域事件。

application

event:事件相关
  1. publish
  2. subscribe
service:应用服务

3.领域层

领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力,用于表达业务概念、业务状态和业务规则
领域层包括聚合根、实体、值对象、领域服务等领域模型中的领域对象。

domain:子目录为聚合aggregate

  1. entity存放聚合根、实体、值对象以及工厂模式相关代码
  2. event存放事件实体以及与实践活动相关的业务逻辑代码
  3. repository存放所在聚合的查询或持久化领域对象的代码
  4. service存放领域服务代码,组合多个实体形成业务逻辑

4.基础层

基础层贯穿所有层,它的作用就是为其他各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库……比较常见的功能是数据库持久化

infrastructure

config:配置相关代码
util:基础代码
  1. api
  2. driver
  3. eventbus
  4. mq

DEMO

DDD架构目录结构】

比如,下图是一个person服务的架构:


DDD服务调用关系】

-------------本文结束感谢您的阅读-------------

本文标题:用人话说明白DDD(领域驱动设计)

文章作者:DragonBaby308

发布时间:2020年01月15日 - 21:08

最后更新:2020年02月14日 - 15:08

原始链接:http://www.dragonbaby308.com/ddd/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%