DDD简介

概述

传统的数据驱动开发模式,View、Service、Dao这种三层分层模式,会很自然的写出过程式代码,这种开发方式中的对象只是数据载体,而没有行为,是一种贫血对象模型。以数据为中心,以数据库ER图为设计驱动,分层架构在这种开发模式下可以认为是数据处理和实现的过程。
在这里插入图片描述
Domain Driven Design,领域驱动设计,来源于Eric Evans的书《Domain-Driven Design –Tackling Complexity in the Heart of Software》。它倡导通过建立领域模型来驱动软件的设计,从业务知识出发设计业务人员和技术人员都能理解的模型。

思想

Alberto(Event Storming的提出者)认为,DDD是一种在面向高度复杂的软件系统时,关于如何去建模的方法论。其关键点是根据系统的复杂程度,建立合适的模型。在一个系统中,没有一个人能完全掌握系统的全貌。在多人参与的系统中,DDD正是可以通过在不同角色之间进行协作,使参与者达成统一认知,对齐系统设计与程序实际所服务的业务领域。
在这里插入图片描述
图片源:https://insights.thoughtworks.cn/ddd-architecture-design/

统一语言是领域驱动设计的关键概念之一。领域建模的核心难点就在于业务和技术人员使用不同的概念(词汇)来描述对软件的理解。领域模型就是跨越业务领域作为问题空间和软件解决方案空间的桥梁。

概念

在这里插入图片描述

在这里插入图片描述

几种域

  • 领域:领域就是范围。范围的重点是边界。领域的核心思想是将问题逐级细分来减低业务和系统的复杂度;
  • 子域:领域进一步划分,得到子领域,即子域。处理高度复杂领域的设计思想,它试图分离技术实现的复杂性;

领域划分过程中,会不断划分子域,子域按重要程度会被划分成三类:

  • 核心域:核心业务服务
  • 通用域:中间件服务或第三方服务;
  • 支撑域:企业公共服务;

限界上下文

Bound Context,BC。

领域模型集合之间由于业务的相关性可能形成松散的边界,这些边界就是分解复杂、大型问题为局部、小型问题的契机。通过辨析模型的相关性,找到边界就能为软件模块的划分、服务的划分提供指导。

在领域驱动中,识别出来的边界被称为限界上下文。

识别上下文不是一件容易的事情,可以从三个方面综合入手:

  • 领域知识
  • 团队合作
  • 技术实现
    在这里插入图片描述

集成上下文的手段:开放领域服务接口、开放HTTP服务、消息发布-订阅机制。

限界上下文之间的映射关系:

  • Partnership:合作关系,两个上下文紧密合作的关系;
  • Shared Kernel:共享内核,两个上下文依赖部分共享的模型;
  • Customer-Supplier Development:客户方-供应方开发,上下文之间有组织的上下游依赖;
  • Conformist:遵奉者,下游上下文只能盲目依赖上游上下文;
  • Anticorruption Layer:防腐层,一个上下文通过一些适配和转换与另一个上下文交互;
  • Open Host Service:开放主机服务,定义一种协议来让其他上下文来对本上下文进行访问;
  • Published Language:发布语言,通常与OHS一起使用,用于定义开放主机的协议;
  • Big Ball of Mud:大泥球,混杂在一起的上下文关系,边界不清晰;
  • Separate Way:另谋他路,两个完全没有任何联系的上下文。

聚合和聚合根

聚合是一组始终需要保持一致的业务对象,有助于保持业务对象的一致性。聚合类似于包,每个包里包含一类实体或行为,有助于分散系统复杂性,也是一种高层次的抽象,可简化对领域模型的理解。聚合是通过定义领域对象之间清晰的所属关系以及边界来实现领域模型的内聚,以此来避免形成错综复杂的、难以维护的对象关系网。

聚合根:Aggregate Root,AR,一个上下文内可能包含多个聚合,每个聚合都有一个根实体,叫做聚合根,一个聚合只有一个聚合根。

聚合根属于实体对象,它是领域对象中一个高度内聚的核心对象。聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识。若一个聚合仅有一个实体,那这个实体就是聚合根;但要有多个实体,就得思考聚合内哪个对象有独立存在的意义且可以和外部领域直接进行交互。

在定义聚合时,应遵守不变形约束法则:

  • 聚合边界内必须具有哪些信息,如果没有这些信息就不能称为一个有效的聚合;
  • 聚合内的某些对象的状态必须满足某个业务规则:
  • 一个聚合只有一个聚合根,聚合根是可以独立存在的,聚合中其他实体或值对象依赖与聚合根;
  • 只有聚合根才能被外部访问到,聚合根维护聚合的内部一致性。

实体和值对象

实体:Entity,当一个对象由其标识(而不是属性)区分时,这种对象称为实体。

值对象:Value Object,当一个对象用于对事物进行描述,而没有唯一标识时,则被称作值对象。

两个容易混淆的概念,对应编码时的Domain或Entity。

区别

实体值对象
有生命周期起描述作用
有唯一标识无唯一标识
通过ID判断相等实现equals方法
CRUD/持久化即时创建用完就扔
可变不可变

领域服务

Domain Service,一些重要的领域行为或操作,而并不是具体的事物,不太适合建模为实体对象或值对象。这些操作往往又会涉及到多个领域对象的操作,它们只负责来协调这些领域对象完成操作而已,可归类为领域服务。它实现全部业务逻辑并且通过各种校验手段保证业务的正确性,同时也能避免在应用层出现领域逻辑。领域服务有点facade意思。

工厂

DDD语境下,工厂(Factory)是一种封装思想的体现。引入原因:有时创建一个领域对象是一件相对比较复杂的事情,而不是简单的new操作。工厂的作用是隐藏创建对象的细节,这样就可以不会让领域层的业务逻辑泄露到应用层,同时也减轻应用层负担,它只要简单调用领域工厂来创建出期望的对象即可。事实上大部分情况下,领域对象的创建都不会相对太复杂,故仅需使用简单的构造函数创建对象就可以。

仓储

Repository,资源仓储封装基础设施来提供查询和持久化聚合操作。这样能够让我们始终关注在模型层面,把对象的存储和访问都委托给资源库来完成。它不是数据库的封装,而是领域层与基础设施之间的桥梁。DDD关心的是领域内的模型,而不是数据库的操作。

Facade

适配层,亦称防腐层。在一个上下文中,有时需要对外部上下文进行访问,通常会引入Facade层的概念来对外部上下文的访问进行一次转义。

考虑引入Facade层的几种情况:

  • 需要将外部上下文中的模型翻译成本上下文理解的模型;
  • 不同上下文之间的团队协作关系,如果是供奉者关系,建议引入防腐层,避免外部上下文变化对本上下文的侵蚀;
  • 该访问本上下文使用广泛,为了避免改动影响范围过大。

如果内部多个上下文对外部上下文需要访问,那么可以考虑将其放到通用上下文中。

两种模型

贫血模型适合简单场景,充血模型更符合DDD理念。选择哪种模型取决于项目的复杂性和团队的理解能力。

贫血模型

指领域对象里只有get和set方法,仅包含状态(属性),不包含行为(方法),业务逻辑通常在服务类中实现。

优点:

  • 简单易懂,代码结构清晰;
  • 业务逻辑与数据分离,便于维护和测试。

缺点:

  • 逻辑分散,可能导致服务类变得庞大且复杂;
  • 不易理解业务规则,因为它们不在模型中体现。

充血模型

指领域对象不仅包含属性,还封装与这些属性相关的业务逻辑。业务逻辑直接在领域对象中实现。

优点:

  • 业务逻辑紧密结合数据,易于理解和维护;
  • 领域模型更能反映真实世界的业务规则,便于团队沟通。

缺点:

  • 可能导致对象变得复杂,增加学习成本;
  • 在某些情况下,领域对象可能会变得庞大,难以维护。

事件风暴

从业务知识中提取出领域模型的方法:

  • 事件风暴
  • 彩色建模:建模方法,通过颜色区分不同领域模型的特点,来澄清领域模型的职责。

Event Storming,一种流行的软件建模方法,利用研讨会(workshop,也可译为工作坊,比较类似于头脑风暴)方式,快速发现软件特定领域中所发生事物的作法;引导业务人员和技术人员共同创作领域模型,以业务事件为线索,探索系统中可能的领域模型。

可供使用的在线工具BeeArt。

延展阅读:事件风暴。

四重边界

在这里插入图片描述
DDD通过规划四重边界,把领域知识进行合理的固化和分层。业务有核心领域和支持域、业务域中又拆分成多个限界上下文(BC),一个BC中又根据领域知识核心与否进行分层,领域层中按照多个业务(子域)的强相关性进行聚合成一个子域:

  • 第一重边界:确定项目的愿景与目标,确定问题空间,确定核心子领域、通用子领域(多个子领域可以复用)、支撑子领域(额外功能,如数据统计、导出报表);
  • 第二重边界:解决方案空间里的限界上下文就是一道进程隔离层面的物理边界;
  • 第三重边界:每个限界上下文内,使用分层架构划分为:接口层、领域层、应用层、基础设施层之间的最小隔离;
  • 第四重边界:领域层里为了保证各个领域的完整性和一致性,引入聚合的设计作为隔离领域模型的最小单元。

架构

分层架构

DDD分层架构中的依赖原则:每层只能与位于下方的层发生耦合,类似于网络的7层或TCP/IP的4层模型架构,每一层各司其职,并且只关心向下一层的实现,不出现各层耦合。

DDD分层架构从上到下包含四层:

  • 用户接入层:用户接口层,处理用户接入的数据结构,如RESTful API,或事件;
  • 应用层:处理用户的业务操作逻辑,也就是用例,它和用户的使用场景相关;
  • 领域层:处理通用的领域逻辑,也就是较为专业的业务逻辑,如订单价格计算;
  • 基础设施层:用来和基础设施适配,如连接数据库,操作Redis等。

在这里插入图片描述

六边形架构

Alistair Cockburn提出六边形架构,将应用分为内六边形和外六边形两层,内六边形实现应用的核心业务逻辑。外六边形完成外部应用,基础资源等的交互和访问,对于与不同的外部系统交互,由外六边形的适配器负责协议转换,保证内六边形业务逻辑的干净。
在这里插入图片描述
适配有两类:

  • 主动适配:指来自于UI、命令行等输入型命令,如Controller就是⼀种端口,端口的具体实现就是应用逻辑自身。端口和具体实现都在应用系统的内部。
  • 被动适配:指访问存储设备,外部服务等。每种访问就是⼀种端口,具体实现是各个具体的中间件。端口在整个应用系统内部,具体实现在系统外部。

每⼀种输入和输出都是⼀个端口,每个端口都有具体的实现逻辑,因此整个应用系统的架构就是⼀些列的端口+适配逻辑组成,架构图就是⼀个多边形形状。有几个端口需要根据应用系统的具体情况⽽定。

特点:

  • 外层依赖内层使得依赖更合理。端口就是接口,依赖接口编程。借此保证应用和实现细节之间的隔离;
  • 可测试性更好。

洋葱架构

在这里插入图片描述
Jeffrey Palermo提出的洋葱架构针对六边形架构更进⼀步把内层的业务逻辑分为DDD概念的应⽤服务层、领域服务层和领域模型层。

洋葱架构根据依赖原则,定义各层的依赖关系,越往里依赖程度越低,代码级别越高,越是核心能力。外圆代码依赖只能指向内圆,内圆不需要知道外圆的情况,都体现高内聚低耦合特性

特点:

  • 围绕独立的领域模型构建应用
  • 内层定义接口,外层实现接口
  • 依赖的方向指向圆心(洋葱架构提倡不破坏耦合方向的依赖都是合理的,外层可以依赖直接内层,也可以依赖更里面的层)
  • 所有的应用代码可以独立于基础设施编译和运行

分层协作

DDD各层的主要职责和分工协作示意图:
在这里插入图片描述
facade接口用于封装应用服务,适配不同前端需要的字段,提供不同要求的服务接口适配。

微服务之间通过消息队列实现解耦。

拓展

Event Sourcing

事件回溯,ES,主要特点是不保存对象的最新状态,而保存对象产生的所有事件,通过事件回溯得到对象最新的状态。通常是在每次对象参与完一个业务动作后,把对象的最新状态持久化到数据库中,即用数据库来反映对象当前最新状态。

一种存储状态变化的方式,所有状态的变化都以事件的形式保存,而不是直接保存当前状态。系统的状态可以通过重放这些事件来恢复。将业务操作记录为一系列事件,确保数据的不可变性和可追溯性。这在需要审计和历史回溯的场景中非常有用。

DDD为Event Sourcing和CQRS提供理论基础。领域模型可以被Event Sourcing和CQRS驱动,从而形成更好的设计。

EDA

Event Driven Architecture,事件驱动架构。

领域事件

DDD+ES的一种架构设计思想。领域事件,见名知意,发生在领域中的一些事件。将领域中发生的活动建模成一系列的离散事件,每个事件都用领域对象来表示,领域事件是领域模型的组成部分。

领域事件有产生、存储、分发和使用等操作,领域事件可由本地BC消费,也可由远程BC消费:
在这里插入图片描述

CQRS

Command and Query Responsibility Segregation,命令查询责任分离,Greg Young最早提出,架构图:
在这里插入图片描述
一种架构模式,将系统的命令(修改数据的操作)和查询(获取数据的操作)分开处理。这种分离允许在不同的模型和技术上优化命令和查询的处理。命令和查询分离使得开发者可以更好地把握对象细节,更好地理解哪些操作会改变系统的状态。

CQRS架构本身只是一个读写分离的思想,实现方式多种多样:

  • 数据库读写分离;
  • 底层存储不分离,但上层逻辑代码分离;
  • 系统底层存储分离,Command端采用Event Sourcing的技术,在EventStore中存储事件;Query端存储对象的最新状态,用于提供查询支持。

CQRS常与Event Sourcing结合使用,因为Event Sourcing提供的事件可以很自然地支持命令和查询的分离。

CRUD的缺陷,也就是引入CQRS的理由举例:

  • 把多条记录合并为一条;
  • 把不同地方的记录整合为一条虚拟记录。

适用场景:

  • 应用的写模型和读模型差别比较大时;
  • 需要对系统的查询和写入性能分开进行优化时,尤其是读写比非常高的系统,分离读写是必须的;
  • 系统需同时满足高并发的读、写时:CQRS架构可在Command端做到最大化的写,Query端容易提供可扩展的读模型;
  • 实践DDD:CQRS架构可让领域模型不受任何ORM框架带来的对象和数据库的阻抗失衡的影响。

实战

解耦合

业务拆分

微服务

用DDD的思想去指导微服务的实践。

中台设计

参考

  • DDD的7大关键概念

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/442545.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaSE - 基础语法

01 背景知识补充 ① Java统治了后台服务器的开发,比如京东,淘宝网站的后台服务器就是使用的Java进行开发的 ② Java之父:詹姆斯高斯林 ③ Java由sun公司研发,现在属于Oracle公司 02 注释 ① Java的注释有三种:单行…

快速启动工具 | Biniware Run v7.1.0.0 绿色中文版

Biniware Run是一款便携式的Windows生产力工具,旨在为用户提供快速访问其喜爱的网站地址、文件和文件夹的便捷方式。这款软件的特点在于其易用性和高度可定制性。用户可以通过简单的拖放操作,将网址、文件或文件夹添加到软件中,从而快速访问。…

网络层协议 --- IP

序言 在这篇文章中我们将介绍 IP协议,经过这篇文章的学习,我们就会了解运营商到底是如何为我们提供服务的以及平时我们所说的内网,公网到底又是什么,区别是什么? IP 地址的基本概念 1. IP 地址的定义 每一个设备接入…

【进阶OpenCV】 (4)--图像拼接

文章目录 图像拼接1. 读取图片2. 计算图片特征点及描述符3. 建立暴力匹配器4. 特征匹配5. 透视变换6. 图像拼接 总结 图像拼接 图像拼接是一项将多张有重叠部分的图像(这些图像可能是不同时间、不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分…

AI学习记录 - L2正则化详细解释(权重衰减)

大白话: 在反向传播时,加入额外的损失值,让总损失值变得比原来更大,并且加入的损失值要关联到神经网络全部权重的大小,当出现权重的平方变大的时候,也就是网络权重往更加负或者更加正的方向走的时候&#…

【答疑解惑】图文深入详解undo和redo的区别及其底层逻辑

题记:最近有些人问我,undo和redo到底是什么关系,他们中不乏已经入行3-4年的同学,今天咱们就来深入探讨下到底什么是undo和redo,他们分别做什么,底层逻辑原理是什么等等。 1. undo 1.1 undo的存储结构 Un…

叶国富“推翻”马云新零售,零售新王此刻登基?

63亿入主永辉超市,拿到29.4%股份,坐上永辉超市第一大股东的宝座,名创优品创始人叶国富,成为了新科“零售之王”。 很是霸气外漏。 有投资者表示费解,不明白为何此时入局超市行业,叶国富当即召开电话会议&…

Selenium自动化测试的显示等待

在进行UI自动化测试的时候,我们为了保持用例的稳定性,往往要设置显示等待,显示等待就是说明确的要等到某个元素的出现或者元素的某些条件出现,比如可点击、可见等条件,如果在规定的时间之内都没有找到,那么…

我们如何构建 ClickHouse 内部的数据仓库:一年回顾的思考 【Part2】

本文字数:4105;估计阅读时间:11 分钟 作者:Mihir Gokhale 本文在公众号【ClickHouseInc】首发 一年前,我的同事 Dmitry Pavlov 介绍了我们如何在 ClickHouse Cloud 上构建了公司内部的数据仓库,简称 “DWH”…

外贸财务管理必备,6款热门软件优势对比

外贸企业的财务管理面临着多币种结算、汇率波动、跨境支付等复杂问题。本文将盘点Zoho Books、KashFlow、Sage Intacct等六款热门的外贸财务软件,并探讨它们各自的优势与特点,以帮助外贸企业做出明智的选择。 一、Zoho Books Zoho Books是一款面向中小企…

RNN(循环神经网络)简介及应用

一、引言 在深度学习领域,神经网络被广泛应用于各种任务,从图像识别到语音合成。但对于序列数据处理的任务,如自然语言处理(NLP)、语音识别或时间序列预测等,传统的前馈神经网络(Feedforward N…

docker compose入门5—创建一个3副本的应用

1. 定义服务 version: 3.8 services:web:image: gindemo:v2deploy:replicas: 3ports:- "9090" 2. 启动服务 docker compose -f docker-compose.yml up -d 3. 查看服务 docker compose ps 4. 访问服务

如何使用jmeter进行压测

简介: 1.概述 一款工具,功能往往是很多的,细枝末节的地方也很多,实际的测试工作中,绝大多数场景会用到的也就是一些核心功能,根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲…

相亲交友系统源码开发:构建高效互动平台的技术探索

在数字化时代,相亲交友系统已成为人们寻找伴侣、拓展社交圈的重要方式之一。这类平台不仅促进了人与人之间的连接,还通过算法匹配、兴趣筛选等功能,提高了用户找到合适伴侣的效率。本文将从技术角度出发,探讨相亲交友系统源码开发…

[paddle]paddleseg快速开始

快速开始 为了让大家快速了解PaddleSeg,本文档使用一个简单示例进行演示。在实际业务中,建议大家根据实际情况进行调整适配。 在开始下面示例之前,请大家确保已经安装好PaddleSeg开发环境(安装说明)。 1 准备数据 …

Java->优先级队列(堆)

一、优先级队列 1.概念 数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数 据结构就是优先级队列(Priority Queue)。 2.堆的概念 把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中 3.堆的性质 …

python中,try-except捕获异常的意义(通过ai智库学习)

python中,不但可以用try-except捕获异常, 还可以自定义异常提示字符串,更可以自定义捕获异常后的处置。 (笔记模板由python脚本于2024年10月03日 06:47:06创建,本篇笔记适合喜欢研究python的coder翻阅) 【学习的细节是欢悦的历程】…

基于SSM车位租赁系统【附源码】

基于SSM车位租赁系统 效果如下: 注册页面 首页展示 车位租赁订单展示 车位列表页面 公告信息管理页面 公告类型管理界面 研究背景 随着经济的持续增长和城市化进程的加速,土地资源变得日益紧缺,停车难问题已成为许多城市面临的共同挑战。随…

【JavaEE】——文件IO

阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 一:认识文件 1:文件的概念 2:文件的结构 3:文件路径…

No package nodejs available.No package npm available.

安装nodejs时出现的报错 这个错误的原因是当前的 yum 源没有包含 Node.js 和 npm 的安装包。 解决方法 使用 NodeSource 仓库 curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -运行 yum install 安装 Node.js 和 npm: yum install -y nodejs使用 E…