【架构】洋葱架构

news2024/9/23 11:26:25

文章目录

  • 前言
  • 一、为什么要用洋葱架构?
  • 二、原则
    • 2.1、依赖性
    • 2.2、数据封装
    • 2.3、关注点的分离
    • 2.4、耦合性
  • 三、洋葱架构层
  • 四、领域模型/实体
  • 五、领域服务
  • 六、应用服务
  • 七、基础设施服务
  • 八、可观察性服务
  • 九、测试策略
  • 十、微服务
  • 十一、模块化与打包
  • 十二、框架、客户端和驱动
  • 十三、我们需要每个层吗?
  • 总结

前言

领域驱动设计(Domain-driven design,DDD)是一种为复杂需求开发软件的方法,它将软件的实现与不断发展的核心业务概念模型紧密地结合在一起。

领域是一个知识的范畴。它指的是我们的软件所要模拟的业务知识。领域驱动设计的中心是领域模型,它对一个领域的流程和规则有着深刻的理解。洋葱架构实现了这一概念,并极大地改善了代码的品质,降低了复杂性,并且支持不断发展的企业系统。

在这里插入图片描述

一、为什么要用洋葱架构?

领域实体是核心和中心部分。洋葱架构是建立在一个领域模型上的,其中各层是通过接口连接的。其背后的思想是,在领域实体和业务规则构成架构的核心部分时,尽可能将外部依赖性保持在外。

  • 它提供了灵活、可持续和可移植的架构。

  • 各层之间没有紧密的耦合,并且有关注点的分离。

  • 由于所有的代码都依赖于更深的层或者中心,所以提供了更好的可维护性。

  • 提高了整体代码的可测试性,因为单元测试可以为单独的层创建,而不会影响到其他的模块。

  • 框架/技术可以很容易地改变而不影响核心领域。例如,RabbitMQ 可以被 ActiveMQ 取代,SQL 可以被 MongoDB 取代。

二、原则

洋葱架构是由多个同心层构成,它们相互连接,并朝向代表领域的核心。它是基于控制反转(Inversion of Control,IoC)的原则。该架构并不关注底层技术或框架,而是关注实际的领域模型。它是基于以下原则:

2.1、依赖性

圆圈代表不同的责任层。一般来说,我们潜入得越深,就越接近于领域和业务规则。外圈代表机制,内圈代表核心领域逻辑。外层依赖于内层,而内层则对外圈一无所知。通常情况下,属于外圈的类、方法、变量和源代码依赖于内圈,但是反过来也一样。

数据格式/结构可能因层而异。外层的数据格式不应该被内层使用。例如,API 中使用的数据格式可以与 DB 中用于持久化的数据格式不同。数据流可以使用数据传输对象。每当数据跨层/跨界时,它应该以方便该层的形式出现。例如,API 可以有 DTO,DB 层可以有 Entity Objects,这取决于存储在数据库中的对象与领域模型的不同。

2.2、数据封装

每个层/圈封装或隐藏内部的实现细节,并向外层公开接口。所有的层也需要提供便于内层消费的信息。其目的是 最小化层与层之间的耦合,最大化跨层垂直切面内的耦合 。我们在较深的层定义抽象接口,并在最外层提供其具体实现。这样可以确保我们专注于领域模型,而不必过多地担心实现细节。我们还可以使用依赖性注入框架,比如 Spring,在运行时将接口与实现连接起来。例如,领域中使用的存储库和应用服务中使用的外部服务在基础设施层实现。

在这里插入图片描述

洋葱架构中的数据封装

2.3、关注点的分离

应用被分为若干层,每一层都有一组职责,并解决不同的关注点。每一层都作为应用中的模块/包/命名空间。

2.4、耦合性

低耦合性,可以使一个模块与另一个模块交互,而不需要关注另一个模块的内部。所有的内部层都不需要关注外部层的内部实现。

三、洋葱架构层

让我们通过一个创建订单的用例来了解架构的不同层和它们的职责。当收到一个创建订单的请求时,我们会对这个订单进行验证,将这个订单保存在数据库中,更新所有订单项目的库存,借记订单金额,最后向客户发送订单完成的通知。

在这里插入图片描述

说明各层之间的依赖关系的包图

在这里插入图片描述

四、领域模型/实体

领域实体是领域驱动设计的基本构件,它们被用来在代码中为通用语言的概念建模。实体是在问题域中具有唯一身份的领域概念。领域实体封装了属性和实体行为。它应该是独立于数据库或网络 API 等特定技术的。例如,在订单领域,订单是一个实体,并具有像 OrderId、Address、UserInfo、OrderItems、PricingInfo 这样的属性以及像 AddOrderItems、GetPricingInfo、ValidateOrder 这样的行为。

在这里插入图片描述

订单实体类

五、领域服务

领域服务负责保持领域逻辑和业务规则。所有的业务逻辑应该作为领域服务的一部分来实现。领域服务由应用服务协调,以服务于业务用例。它们不是典型的 CRUD 服务,通常是独立的服务。领域服务负责复杂的业务规则,如在处理订单时计算价格和税收信息,保存和更新订单的订单库接口,更新购买物品信息的库存接口等。

它包含了对其目标非常关键的算法,并且将用例作为应用的核心来实现。

六、应用服务

应用服务也被称为“用例”,是只负责协调请求步骤的服务,不应该有任何业务逻辑。应用服务与其他服务交互,以满足客户的请求。让我们考虑一下用例,用一个物品清单创建一个订单。我们首先需要计算价格,包括税收计算/折扣等,保存订单项目并向客户发送订单确认通知。定价计算应该是领域服务的一部分,但涉及定价计算、检查可用性、保存订单和通知用户的协调工作应该是应用服务的一部分。应用服务只能由基础设施服务调用。

七、基础设施服务

基础设施服务也被称为基础设施适配器,是洋葱架构的最外层。这些服务负责与外部世界交互,不解决任何领域的问题。这些服务只是与外部资源通信,没有任何逻辑。例如:外部通知服务、GRPC 服务器端点、Kafka 事件流适配器、数据库适配器。

八、可观察性服务

可观察性服务负责监控应用。这些服务有助于执行以下任务:

数据收集(指标、日志、痕迹):主要使用库/侧线来收集代码执行期间的各种数据。

数据存储:使用能够集中存储所收集的数据的工具(分类、索引等)。

可视化:使用允许你对收集的数据进行可视化的工具。

一些例子包括 Splunk、ELK、Grafana、Graphite、Datadog。

九、测试策略

洋葱架构的不同层有不同的职责,相应地也有不同的测试策略。测试金字塔是一个很好的框架,它规定了不同类型的测试。属于领域模型、领域服务和应用服务的业务规则应通过单元测试进行测试。当我们移动到外层时,在基础设施服务中进行集成测试更有意义。对于我们的应用,端到端测试和 BDD 是最合适的测试策略。

在这里插入图片描述

针对不同层的测试策略

十、微服务

当孤立地看待每个微服务时,洋葱架构也适用于微服务。每个微服务都有自己的模型、自己的用例,并定义了自己的外部接口,用于检索或修改数据。这些接口可以用一个适配器来实现,该适配器通过公开 HTTP Rest、GRPC、Thrift Endpoints 等连接到另一个微服务。它很适合微服务,在微服务中,数据访问层不仅包括数据库,还包括例如一个 http 客户端,以从另一个微服务,甚至从外部系统获取数据。

应用结构和层数
在这里插入图片描述
在这里插入图片描述

应用结构和层,包括层如何映射到模块以及它们之间的依赖关系。它还描述了对不同层使用什么样的测试策略

十一、模块化与打包

有两种方法来组织应用的源代码:

要么,我们可以将所有的包放在一个模块/项目中,要么将应用分为不同的模块/项目,每个模块/项目负责洋葱架构中的一个层。

这在很大程度上取决于应用的复杂性和项目的规模,将源代码分为多个模块。在微服务架构中,模块化可能有意义,也可能没有意义,这取决于复杂性和用例。

十二、框架、客户端和驱动

基础设施层由网络或服务器的框架、数据库的客户端、队列或外部服务组成。它负责配置和缝合所有的外部服务和框架。洋葱架构提供了解耦功能,因此在任何时候交换技术都会变得更容易。

十三、我们需要每个层吗?

将我们的应用分层组织有助于实现关注点的分离。但我们需要所有的层吗?也许需要,也许不需要。这取决于用例和应用的复杂性。根据应用的需要,也可以创建更多的抽象层。例如,对于没有很多业务逻辑的小型应用,拥有领域服务可能没有意义。无论哪一层,依赖关系都应该是从外层到内层。

总结

洋葱架构在开始时可能似乎有些困难,但是 在业界已经得到了普遍的认可 。这是一种让软件易于演进的强有力架构。通过把应用划分为几层,可以使系统更加易于测试、维护和移植。它有助于在旧框架过时时轻松采用新框架/技术。与其他架构风格类似,如六边形、分层、简洁的架构等,它为常见问题提供了一个解决方案。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java个人博客网站设计实现(源码+lw+部署文档+讲解等)

博主介绍: ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精…

基于Java游戏攻略网站设计实现(源码+lw+部署文档+讲解等)

博主介绍: ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精…

【Unityc#专题篇】之c#进阶篇

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

【C#进阶】C# 索引器

序号系列文章13【C#进阶】C# 特性14【C#进阶】C# 反射15【C#进阶】C# 属性 文章目录 前言1、索引器的概念2、索引器的定义3、索引器的基本使用4、索引器的重载5、接口中的索引器6、属性和索引器之间的比较7、索引器的适用场景结语 前言 🍂 Hello大家好啊&#xff0c…

基于Java会员管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍: ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精…

从零开始 Spring Boot 46:@Lookup

从零开始 Spring Boot 46:Lookup 图源:简书 (jianshu.com) 在前文中,我介绍了 Spring Bean 的作用域(Scope),且讨论了将一个短生命周期的 bean (比如request作用域的 bean)注入到长…

事务小总结

事务定义 是一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作(程序执行)单元。事务由事务开始与事务结束之间执行的全部数据库操作组成。 事务特性 原子性(Atomicity)一致性(Consistency)隔离性(…

Linux下 文件删除但是空间未被释放 或者 磁盘已满但找不到对应的大文件 的解决方案

Linux下文件删除但是空间未被释放的解决方案 前言1. 查看当前磁盘占用情况2. 模拟进程占用3. 执行rm -rf 命令删除文件4. 查看被删除但是未释放空间的文件5. 执行清空文件操作 前言 linux磁盘空间已满,手动rm -rf 删除了大文件之后,df -h 查看一下发现空…

操作系统第四章练习题

第一部分 教材习题(P152) 1、为什么要配置层次式存储器? 设置多个存储器能够使存储器两头的硬件能并行工作;采用多级存储系统,专门是Cache 技术,是减轻存储器带宽对系统性能影响的最佳结构方案;在微处置机内部设置各类缓冲存储器,减轻对存储…

【Android -- 面试】Android 面试题集锦(Java 基础)

Java 基础 1、Java 的类加载过程 jvm 将 .class 类文件信息加载到内存并解析成对应的 class 对象的过程, 注意:jvm 并不是一开始就把所有的类加载进内存中,只是在第一次遇到某个需要运行的类才会加载,并且只加载一次 主要分为三…

【ARMv8 SIMD和浮点指令编程】NEON 比较指令——比较的方方面面

比较指令是常见的一类指令,NEON 中当然也排除,下面涉及比较和测试位两类指令。 1 CMEQ (register) 按位比较相等(向量)。该指令将第一个源 SIMD&FP 寄存器中的每个向量元素与第二个源 SIMD&FP 寄存器中的相应向量元素进行比较,如果比较相等,则将目标 SIMD&…

基于Python+Django+mysql+html通讯录管理系统

基于PythonDjangomysqlhtml通讯录管理系统 一、系统介绍二、功能展示1.用户登陆2.用户注册3.密码修改4.查询5.添加6.修改7.删除 三、其它系统四、获取源码 一、系统介绍 该系统实现了 用户登陆、用户注册、密码修改、查询信息、添加信息,修改信息、删除信息 运行环…

vue2脚手架可视化小项目

vue2脚手架小项目 这个项目又数据可视化可以通过点击按钮修改数据,同时图形的内容也会随着变化这里面也又保存功能,搜索功能,添加功能用到的框架有vue2脚手架,语言有bootstrap,css,也用了echarts 下面会先…

从Web2.0走向Web3.0还有多远?

Web2.0时代给互联网带来了巨大的变革,让用户成为内容的创造者和共享者。然而,随着技术的不断发展和创新,我们正在逐渐迈向Web3.0时代,这将是一个更加去中心化、透明和安全的数字世界。那么,从Web2.0走向Web3.0还有多远…

202322读书笔记|《给你一个大抱抱:治愈系暖心绘本》——如果事与愿违,请相信一切自由安排

[TOC](202322读书笔记|《给你一个大抱抱:治愈系暖心绘本》——如果事与愿违,请相信一切自由安排 作者白开水DAWAN,治愈系小说,画面明媚有趣轻松🤪🤪😘&#…

learn C++ NO.10——string(3)

引言: 现在是北京时间2023年6月22日的早上8点。又是一年端午,时光如梭。这一年来发生的变化太多了,遥想去年此时,我还沉浸在被大学录取的喜悦中,转眼间大一就过去了。这里我也衷心的祝愿您和您的家人端午安康&#xf…

Triton教程 --- 自定义操作

Triton教程 — 自定义操作 Triton系列教程: 快速开始利用Triton部署你自己的模型Triton架构模型仓库存储代理模型设置优化动态批处理速率限制器模型管理 自定义操作 Triton 推理服务器部分支持允许自定义操作的建模框架。 自定义操作可以在构建时或启动时添加到 Triton&…

前端3D Three.js 在本地搭建一个官方网站

上文Web前端 3D开发入门规划 3D效果将不再是桌面应用的专利我们说了说 WEB 3D的一个发展和学习方向 那么 我们还是先搞定文档使用的问题 我们可以访问http://www.yanhuangxueyuan.com/Three.js/这里面可以查到使用方法 但不是最新的 而是别人用官方文档改的 真正的官方文档地址…

30 linux 新建进程的进程号是如何分配的?

前言 呵呵 进程号是我们经常使用到的, 但是 却从来 没有深究过的东西 这里 就来看一下 具体的进程号的生成方式 linux 新建进程是以 fork exec 的形式创建进程的 子进程 是 复制自 父进程 pid 是怎么生成的? 分配 pid 的地方调用堆栈如下, fork 之后, 会为 新进程 申…

全志V3S嵌入式驱动开发(基于usb otg的spi-nand镜像烧入)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们说过spi nand驱动,但是这个驱动是建立在linux系统从sd卡加载的情况下进行的。开发测试的情况下,这么做或许是可以的…