领域驱动设计-架构篇
目录
1、软件架构概述
1.1 软件架构概念
1.2 软件架构分类
1.3 软件架构模式
1.4 软件架构风格
2、领域驱动软件架构
2.1 架构风格
六边行架构(领域驱动设计首选)
为什么选择REST架构
松耦合
可伸缩性
易用性
约束性
2.2 架构模型
命令和查询职责分离(CQRS)
大家好,我是老王随聊。筹备了一周之久的架构篇终于出炉了……
前面我们介绍了关于领域驱动设计业务战略层面的几个概念:领域、子域、界限上下文以及界限上下文映射图。那接下来我们要知道如何基于技术战略层面进行领域驱动软件架构设计。本篇主要内容包含两部分:软件架构概述和领域驱动软件架构。
下图是从软件架构层面整理的DDD架构图(完善中)。

在开始讲领域驱动架构之前,我们需要先了解关于软件架构的一些常见架构风格和模型,因为这些内容也将会在领域驱动设计中使用到,便于我们更好的了解这些架构模型在领域驱动中应该如何发挥其价值,如何集众所长。另外,即便是领域驱动设计这种新的方法论,在技术层面也依然采用现有的一些常见设计架构思想,无非就是新瓶装旧酒,关键在于酒怎么装的问题。
1、软件架构概述
1.1 软件架构概念
在软件工程领域,“架构”主要是指软件架构,其主要目的在于指导架构师和开发人员如何进行软件设计,也就是为我们提供软件系统各个方面的设计方向。
软件架构,并不是指可编码能实际落地的文档,而是一个系统的草图。它的主要工作是对一系列相关业务的一种抽象建模,把各种对象抽象成可直接构成系统应用的组件,并对各组件之间如何映射、如何通信进行了明确且细致的描述。最终,在实现编码阶段,这些抽象组件将被细化为具体的某个类或者对象。
那在领域驱动设计中,除了我们看到的软架构外,还隐含着另外一种架构—即业务架构。该业务架构的设计思想将伴随着整个业务系统的生命周期,关于业务架构方面的内容我在后面的文章会讲到。
1.2 软件架构分类
通常一个良好的软件架构,需要具备以下特性:可靠性、安全性、扩展性、可定制化、可伸缩、可维护、易用性和市场机制这八个特点,不同的软件架构模式侧重的特点不同。
从我们日常所关注的角度来看,大体可以分为三类:逻辑架构、物理架构和系统架构。
逻辑架构指的是软件系统中元件之间的关系。比如常用的分层架构就是一种逻辑架构,每一次包含多个逻辑元件;物理架构通常是指软件系统在硬件上的部署方式。比如微服务架构、垮机房垮区域的分布式架构等;系统架构指的是系统非功能性特征,比如云架构,考虑系统的稳定性、可扩展等。
1.3 软件架构模式
从软件架构模式角度看,大致可以划分为:分层模式(常用的标准架构)、客户端/服务模式、事件总线模式、管道和过滤器模式、微核模式、微服务模式和云模式等;
分层架构,将软件分成若干个水平层,每一层都有清晰的角色和分工,不需要知道其他层的细节,层与层之间是通过接口通信;
事件驱动架构,就是通过事件进行通信的软件架构,主要用于系统之间解耦或异步任务处理;
微核模式,也叫插件模式,指的是软件的内核相对较小,主要功能和业务逻辑都通过插件实现,主要用于提升系统组件单元的可插拔性;
云架构,主要解决系统的扩展性和并发的问题,是最容易扩展的架构;
管道和过滤器模式,是面向数据流的软件体系结构,其优点将整个系统的输入输出行为理解为单个过滤器行为的叠加与组合,目的在于将复杂问题分解,做到化繁为简的效果。
1.4 软件架构风格
从架构风格的抽象纬度划分,常见的分布式应用架构风格有以下三种:
分布式对象(简称DO),它要解决的主要问题是位于不同进程中的对象之间的调用问题,常见的架构实例有CORBA、RMI、EJB、DCOM、NET Remoting等。
远程过程调用(简称RPC),远程过程调用采用客户机/服务器(C/S)模式。请求程序就是一个客户机,而服务提供程序就是一台服务器。和常规或本地过程调用一样,远程过程调用是同步操作,在远程过程结果返回之前,需要暂时中止请求程序。使用相同地址空间的低权进程或低权线程允许同时运行多个远程过程调用。常见的架构实例有SOAP、XML-RPC、Hessian、DWR等。WEB服务提供一个分布式函数或方法接口供用户调用,这是一种比较传统的方式。通常,在WSDL中对RPC接口进行定义(类似于早期的XML-RPC)。
表述性状态转移(简称REST),WEB服务类似于HTTP或其他类似协议,它们把接口限定在一组广为人知的标准动作中(比如HTTP的GET、PUT、DELETE)以供调用。此类WEB服务关注与那些稳定的资源的互动,而不是消息或动作。此种服务可以通过WSDL来描述SOAP消息内容,通过HTTP限定动作接口;或者完全在SOAP中对动作进行抽象。架构实例有HTTP、WebDAV。
不同架构模式和风格会之间有非常大的差别。这里暂且不聊对比差异,先了解在领域驱动设计该如何使用这些架构模式和架构风格。
2、领域驱动软件架构
2.1 架构风格
六边行架构(领域驱动设计首选)
我们知道,六边形架构是一种对称性的架构风格,其原理采用了端口适配器模式。其目的在于构建一种持久生命力的架构,各端口之间是一种平等方式与系统交互,是内部和外部的关系,不存在前端和后端严格划分的概念。六边形架构如下图所示。

那为什么领域驱动设计会将六边形架构作为首选?
核心原因在于,领域驱动设计与传统的分层架构相比,其希望我们将重点的工作放在领域层设计上。领域层、基础设施层只依赖由领域模型所定义的抽象接口,而客户层是一种平等的方式与业务模型进行交互。因此与传统风层结构相比,其采用了依赖倒置原则。以上的交互和依赖倒置正好与六边形架构,所以这才是真正选择六边形架构的原因。
当然,如果你的项目采用了领域驱动设计的方式,不一定必须采用六边形架构,具体采用哪种风格需要我们在做架构设计前,从性价比通盘考虑后再选择。
另外需要注意的一点,即使你的系统采用了六边形架构,那同时也依然可以在六边形架构内部使用其他类型架构。比如SOA架构、REST或者事件驱动架构,也有可能采用CQRS;或者数据网织或基于网格的分布式缓存;还有可能采用Map- Reduce这种分布式并行处理方式。六边形架构可为系统其他架构提供坚实的基础支撑,这也体现了六边行架构很好的包容性。
为什么选择SOA架构
面向服务的体系结构(SOA),是一个组件模型,它将应用程序的不同功能(即服务)通过服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以以一种统一和通用的方式进行交互。

这里提到SOA架构,主要是与从六边形架构相比,它们都为领域驱动设计提供了共同的基础价值。
在SOA架构中,这8大设计原则依然适应六边形架构,而且有一部分特性对六边形架构进行了很好的补充。比如服务抽象、松耦合、服务重用性、服务组合性、服务契约、服务自治性、服务无状态性、服务可发现性,这些特性均可以与六边形架构进行很好的结合。在上图中,服务边界位于最左侧,而领域模型位于中心位置,消费方可以通过REST、SOAP和消息机制获取服务。
为什么选择REST架构
REST,表述性状态传递,属于Web架构的一种软件架构风格,REST也是web架构的理论扩展。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
在了解领域设计驱动之前,要说明两点:
第一,REST并不是一种具体的技术,也不是一种具体的规范,而是一种内涵非常丰富的架构风格,一整套研究和评价软件架构的方法论,这套方法论的核心词在“架构风格”。
第二,架构风格是一种研究和评价软件架构设计的方法,它是比架构本身更加抽象的概念。一种架构风格是由一组相互协作的架构约束来定义的,而架构约束是对软件的运行环境施加在架构设计之上的一种约束行为,对架构进行更好的规范。
尤其是第二点,对于我们进行软件设计所起到的作用更大。

在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,当前很多企业开发系统,越来越多的web服务开始采用REST风格设计和实现。
我们知道,在 REST 架构风格中,数据和功能被视为资源,并使用统一资源标识符 (URI) 进行访问。通过使用一组简单的、定义良好的操作来处理资源。客户端和服务器通过使用标准化的接口和协议(通常是 HTTP)来交换资源。另外,其资源是与表现形式进行分离的,以便可以以各种格式访问其内容,例如 HTML、XML、纯文本、PDF、JPEG、JSON 等。
那REST在领域驱动设计中的作用是什么呢?依然是其与六边行架构所提供的共同价值所决定的,即松耦合、可伸缩和易用性。
松耦合
符合REST原则的系统具有更好的松耦合。通常来讲,添加新资源并在已有资源中创建到新资源的链接是非常简单的,要添加新的格式同样如此。REST API使用URI来进行资源的定位,所以这个URI需要直观并且容易使用。
可伸缩性
基于基于REST的系统也是非常容易理解的。因为此时系统被分为很多较小的资源模块,每一个资源块都可以独立测试和调试,并且每一个资源模块都表示了一个可重用的入口点。该架构本身具有很好的松耦合和可伸缩性。
易用性
采用REST架构风格,对于开发、测试、运维人员来说,都会更简单。可以充分利用大量HTTP服务器端和客户端开发库、Web功能测试/性能测试工具、HTTP缓存、HTTP代理服务器、防火墙。这些开发库和基础设施早已成为了日常用品,不需要什么火箭科技(例如神奇昂贵的应用服务器、中间件)就能解决大多数可伸缩性方面的问题。
约束性
另外,REST架构风格最重要的架构约束有6个:客户-服务器(Client-Server)、无状态(Stateless)、通信的会话状态(Session State)、缓存(Cache)、统一接口(Uniform Interface)、分层系统(Layered System)。这些特性对六边形架构也进行了很好的规范约束。
2.2 架构模型
我们前面提到,架构模式通常分分层模式(常用的标准架构)、客户端/服务模式、事件总线模式、命令和查询职责分离模式、管道和过滤器模式、微核模式、微服务模式和云模式等。从作者的视角,为什么要单独大篇幅去讲命令查询模式呢?这是我们需要重点关注的地方。
命令和查询职责分离(CQRS)
CQRS 是“命令和查询责任分离”的英文缩写,它是一种将数据存储的读取操作和更新操作分离的模式,类似于我们在数据库层面的读写分离。它主要解决了,在复杂系统中当多种读取形式和写入工作负载非对称,并且读写的性能要求有很大差异时,会倒置模型执行太多操作且过度复杂的问题。这样做的好处就是可以最大限度地提高系统的性能、可缩放性和安全性。

但CQRS这种模式在领域驱动设计当中是最优的吗?凡事都有两面性,固然优点需要借鉴学习,但也需要我们用批判精神来看待其不足。那CQRS在领域驱动设计中不好的一面也体现的会更明显。
第一,CQRS无法很好的解决事务性。
对于之前采用的单一数据源,我们通常依靠关系型数据库的事务特性能够很好的保证数据的完整性。但是在 CQRS 中这一切都发生了变化。因为在CQRS中一个 command 触发的事件,在 query 端可能需要更新很多个数据模型,而这是有可能失败的。一旦更新失败那么数据就会长时间的处于不一致状态,这时需要外部的介入单独处理。从事务的角度来看 CQRS,需要面对的是问题是如何解决最终一致性。
第二,数据时效性问题。
在 CQRS模式中,当command 端完成数据更新后,需要通过事件形式通知查询端系统,这也就意味着系统之间会存在一定时间差,此时如果业务对于数据的实时性要求非常高,那么可能 CQRS 的技术架构选型就不适合了,此时可能需要对实时的数据接口进行区分加以特殊处理。
第三,查询模式设计的复杂性问题。
虽然 CQRS 为我们分离了领域模型和服务于查询功能的数据模型,但这意味着我们需要单独设计另一套针对查询功能的数据模型。
这种做法带来的问题就是当查询接口越来越多时就会难以管理,仍然需要按照 DDD 中划分领域的思路将属于一个领域的查询集中管理作为整个查询系统的一个上下文,甚至需要独立出一个新的微服务,这也无形之中,CQRS 在带来架构自由与便利的同时也不可避免的引入了额外的复杂性与技能要求。
那作者为什么还要在DDD中提及CQRS架构模式呢?
我个人理解主要有以下两点:
1)选择与领域驱动契合的点—边界清晰职责明确
如果你所面对的业务系统已经非常庞大,而且业务流程庞杂逻辑繁琐,那么不妨尝试使用 CQRS 将 Command 与 Query 进行拆分,将领域模型与数据模型的边界划分的更清晰些。
2)领域驱动架构设计需要集众所长
从作者所的文章中,我们看到无论采用CQRS、消息总线模型、长时处理模型哪种单一架构模型,都不能完美解决系统中所存在的各种问题。所以需要我们使用多种架构模式来共同协作,以此完善整个系统架构。
总之,在软件架构设计中,无论是架构风格还是架构模式,都需要集众所长,避其所短,方能设计出架构良好的系统。
相关文章:
领域驱动设计-架构篇
目录 1、软件架构概述 1.1 软件架构概念 1.2 软件架构分类 1.3 软件架构模式 1.4 软件架构风格 2、领域驱动软件架构 2.1 架构风格 六边行架构(领域驱动设计首选) 为什么选择REST架构 松耦合 可伸缩性 易用性 约束性 2.2 架构模型 命令和…...
docker安装kafka
前言最近在用kafka做项目,所以本地搭建下kafka,但是又嫌java安装和安装kafka太麻烦,所以想到用docker来部署。镜像wurstmeister/kafka维护较为频繁的一个Kafka镜像。只包含了Kafka,因此需要另行提供ZooKeeper,推荐使用…...
Selenium4+Python3系列(十一) - Page Factory设计模式
写在前面: Page Object模式,目的是将元素定位和元素操作分层,只接触测试内容,不写基础内容,便于后续对自动化测试用例体系的维护,这是中心思想,也是核心。 那么我们继续将简洁延续,…...
C++基础知识【4】函数及参数
目录 一、函数的基本概念 1.1、构成 1.2、声明和定义 1.3、函数的调用 二、参数 2.1、形参和实参 2.2、参数的传递 传值 传引用 传指针 三、C函数的一些新特性 3.1、Lambda表达式 3.2、右值引用 3.3、默认参数 3.4、变长参数模板 3.5、constexpr函数 3.6、noex…...
约瑟夫森磁效应
电流与波函数的相位有直接的关系,可得约瑟夫森结的电流为 IIcsinϕ\begin{align} II_c sin\phi \end{align} IIcsinϕ 式中,IcI_cIc为临界电流,相位差为ϕϕ2−ϕ1\phi\phi_2-\phi_1ϕϕ2−ϕ1。 根据磁矢势A的定义,B…...
什么是L1和L2正则化,以及它们有什么区别
一、L1和L2正则化是什么? 在防止过拟合的方法中有L1正则化和L2正则化,L1和L2是正则化项,又叫做惩罚项,是为了限制模型的参数,防止模型过拟合而加在损失函数后面的一项。 在二维的情况下,黄色的部分是L2和…...
场景式消费激发春日经济,这些电商品类迎来消费热潮
春日越临近,商机越浓郁。随着气温渐升,春日经济已经潜伏在大众身边。“春菜”、“春装”、“春游”、“春季养生”等春日场景式消费走热。 下面,鲸参谋为大家盘点几个与春日经济紧密相关的行业。 •春日仪式之春游踏青 ——户外装备全面开花…...
[2.1.4]进程管理——进程通信
文章目录第二章 进程管理进程通信(IPC)为什么进程通信需要操作系统支持?(一)共享存储(1)基于存储区的共享(2)基于数据结构的共享(二)消息传递什么…...
ChatGPT也有犯晕的时候
前面测试 ChatGPT 进行写代码、优化代码、解释代码、一般问答都表现的很好。偷个懒,用ChatGPT 帮我写段生物信息代码如果 ChatGPT 给出的的代码不太完善,如何请他一步步改好?代码看不懂?ChatGPT 帮你解释,详细到爆&…...
机器学习与目标检测作业:连通块算法
机器学习与目标检测作业:连通块算法一、连通块算法题目描述二、连通块算法文件结构三、连通块算法程序编写3.1、连通块算法conBlock.h头文件内容3.2、conBlock.cpp源文件内容3.3.3、mian.h头文件内容3.3.4、main.cpp源文件内容如下四、连通块算法程序运行结果一、连…...
HBase基础 --- 增删查改
目录 创建表 查看指定表全名空间中的表 查看表描述 禁用/启用 查看禁用/启动状态 删除表 新增列族 删除列族 更改列族存储版本的限制 增加数据 根据条件查询 查看指定列中不同版本的数据 删除指定列族下的指定列 删除指定行 全表扫描 全表扫描指定列族…...
如何基于AI智能视频技术实现公园景区的人流量实时统计?
一、方案背景春暖花开的季节来临,外出旅游的人群也越来越多。无论是景区、公园、博物馆、步行街等场所,客流超载非常大,给游客带来的体验较差,同时也存在安全隐患。当前景区面临的管理痛点包括:客流信息查询难…...
【JavaWeb】Servlet详解
文章目录1. 前置知识2.servlet生命周期2.1 默认情况下,服务器启动时,servlet对象并没有被创建2.2 用户执行一次请求2.3用户执行第二次请求2.4 3,4,5,6....次请求2.5 关闭服务器3.servlet方法解析4.适配器模式改造servlet4.1不使用servlet模式4.2使用适配…...
谁是世界上最好的编程语言?--编程语言70年浅谈
1、编程语言发展史纵览 严谨起见,本文提到的编程语言指的是「第三代高级编程语言」。 首先,我们从时间维度入手聊聊编程语言。一图胜千言,我们从目前主流的编程语言中,挑选出流行的、具有历史影响力的语言。把它们按时间从上往下…...
Webpack前端资源加载/打包工具
文章目录一、Webpack1、什么是Webpack2、Webpack安装2.1全局安装2.2安装后查看版本号3、创建项目3.1初始化项目3.2创建src文件夹3.3 src下创建common.js3.4 src下创建utils.js3.5 src下创建main.js4、JS打包4.1创建配置文件4.2执行编译命令4.3创建入口页面4.4测试5、CSS打包5.1…...
springcloud3 fegin实现服务调用1
一 Fegin的作用 1.1 fegin的作用 fegin是一个声明式的web服务客户端,让编写web服务器客户端变得非常容易,只需创建一个接口并在接口中添加FeginClients注解即可。 Fegin的使用方式:使用fegin的注解定义接口,调用这个接口&#…...
专业版即将支持自定义场景测试
物联网 MQTT 测试云服务 XMeter Cloud 专业版于 2022 年底上线后,已有不少用户试用,对数千甚至上万规模的 MQTT 并发连接和消息吞吐场景进行测试。同时我们也收到了希望支持更多物联网协议测试的需求反馈。 新年伊始,XMeter 团队全力聚焦于 …...
Process Monitor工具使用实验(23)
实验目的 学习Process Monitor实用小工具的使用,学会利用Process Monitor工具观察程序进程/线程、文件系统、注册表、网络连接等的活动。预备知识 Process Monitor是一个Windows系统下先进的监视工具,它可以显示文件系统、注册表、网络连接、进程…...
钓鱼客服到拿下服务器全过程(重点在于钓鱼添加img src)
重点总结 钓鱼时主动在变量中添加了字段,等待用户点击获取ip信息进行下一步资金盘plus呢 左看右看没啥东西,看看客服系统能不能打xss。 吊毛客服居然不在线,这套客服系统见过是whisper,之前审计过没有存储xss 但能通过伪造图片…...
【C++】list迭代器的深度剖析及模拟实现(感受类封装,类和对象的思想)
早点睡兄弟,别一天到晚就熬夜。 文章目录一、通过list迭代器来感受类和对象以及类封装的思想1.迭代器的特征和本质是什么?(两大特征:类的内嵌类型,行为像指针。本质:内置类型定义的变量或自定义类型实例化…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
