DDD架构实践
ddd架构浅析
背景介绍
什么是ddd架构,是以ddd思想为参考,做出一份符合ddd思想的框架。
随着技术的迭代升级,越来越多的瓶颈暴露出来,性能瓶颈,系统复杂度瓶颈,这些都逐一被迭代出的技术产物解决。最终的一个产物微服务,几乎完美解决了性能和系统复杂度的问题,保证服务性能的前提下,还简化了系统的复杂度,降低开发难度,让系统更加容易维护。
但是对于B端企业来说,单单解决性能和系统复杂度问题还远远不够,极度复杂的业务,让系统难以维护,动辄几百上千行的方法,让开发人员头疼。每一个简单逻辑的背后,都会存在多个业务方,多个业务方的兼容使得逻辑变得复杂,加上业务的变化频繁这一特点,让系统的复杂度极速提高。
ddd很早就出现了,由于过于复杂难以理解,一直没有很好地最佳实践。ddd的核心思想,就是为了解决B端企业的业务复杂度而生,近些年ddd的突然爆火,是因为有了最佳实践的落地,比较出名的比如cola的应用分层框架。
ddd核心分模块思想
ddd的思想核心是通过领域建模、分层分包的模式来进行降低业务复杂度,简化开发逻辑,清晰业务流向。
通过cola的最佳实践,可以把ddd进行落地,并且由核心和拓展几个点来进行分析。
核心模块
核心分包为 适配器、领域、基建包,
三个包都不相互依赖,形成相互隔离的几个单独模块,让每一个模块都只有很单一的能力,这样就可以很好的把业务限制在领域中,极好的来沉淀建立自己的核心业务能力。
适配器层:
由于B端业务多样性且复杂,所以需要单独的适配器来做,将不同的业务来进行适配,进入自己的核心且单一的能力中。
通过将单一能力,增加多个适配器,提高业务的适配能力。b端业务的多样性,往往体现在数据及命名的多样性,但核心的业务大体相同,所以通过适配器,可以极大降低因多样性带来的复杂度。
领域层:
领域层的单独存在,一方面是为了限制核心业务不会散落的到处都是,另一方面是让开发更加聚焦在核心业务的开发上。通过一些策略及拓展模式,就可以实现核心业务及其不同的特性。
B端企业级对外提供的能力一定是单一的,只有这样才能逐渐沉淀自己在行业内的影响力,如果什么都能支持,反而会觉得什么都不强,都不稳定。而且随着差异太大的业务,会导致成本剧增。
单一的能力,会逐渐去完善产品能力,最终沉淀产出一个标准的行业产品。
在大能力不变的情况下,不同的特性处理,通过一些拓展点,就可以快速实现接手。
基建层:
在B端业务中,三方依赖的侵入性变动也是对业务稳定性的一个侵蚀,比如业务前期,使用某种组件可以满足业务需求,随着发展需要更换另一种组件,这种组件变更给系统带来的稳定性影响是非常重要的,因为组件的变更导致业务宕机是非常不合理的,为了避免这种情况,将三方组件完全隔离是非常必要的。
三方组件包括但不限于:数据库、缓存、mq、三方接口、三方sdk、三方rpc等。
可以认为所有和外部交互的都应该限制在基建层,以此来进行隔离,保证系统的稳定性。
domain模块
domain是三个模块的轴承,通过依赖倒置,让三个模块相互隔离,全都由domain拉着,通过domain进行交互。
domain的必要性:
从物理层面进行隔离,让其他人不能使用其他模块的东西。
pom的变更应该提起cr人员的注意,切实做好每一个pom变更,避免在代码中引入坏味道。
引入之后通过规约来限制是很不合理的,根据墨菲定律,只要存在,就一定会被人使用到。
非核心模块
client模块:
如果系统有对外提供的能力,比如rpc接口,mq实体定义等。
这些内容是切面与系统存在的,需要提供给外部引用,应该单独出来,避免把自己的核心内容给外部使用。
优势:
- 避免由于外部使用不当,导致自己内部的实体变更,让交互不稳定。
- 避免由于提供依赖太大,导致对方系统的负担
- 避免核心能力让对方引用,增加安全风险
遵守规则:
- client包中,不应有任何三方依赖,只是一个简单接口及实体的定义
- 不应有内部使用的包,只需要提供外部所需的即可,遵守最小可用原则
- 统一出参封装,所有对外提供的接口应统一封装,方便框架层面做统一处理。
common模块
对应本应用而言,在不同模块中都有使用到的工具,分别定义是不合适的,通过common包来抽象出共有能力,来降低代码重复率。
ext模块
如果业务的多样性非常复杂,对每一个特性的处理都需要大量的业务逻辑,那么ext模块单独抽出就很有必要。多样性处理的太多,和核心业务掺杂在一块,随着业务的发展,就会产生特性分支和主分支分不清的情况,混用,职责不清晰,能力不单一。即使规约的再好,根据墨菲定律,这种问题一定会发生。
另外一些高阶的玩法,ext模块外置,核心能力很稳定,拓展能力变动较大时,ext模块会成为热插拔组件,通过快速迭代更新来满足业务,做到完全的系统热插拔,不影响当前业务,新的业务快速上线。
也为了避免不同特性分支相互影响,通过这种做到完全隔离。
sop模块
核心业务稳定,且领域模型较为完善,业务进入后期阶段,新的业务接入就不再依托于编码,而且通过流程引擎来做编排,让新的业务进入。
同一个业务能力,有多种处理,且都为核心能力,此时就应该把这个作为一个sop,把这些提出来一个sop模块单独处理,原本核心的领域层,做流程编排及其他前置业务处理。拓展点再次升级为某个sop的拓展能力。
一般情况下,sop是ext的发展产物,某个ext的特性分支业务发展很好,逐渐成长为核心业务,趋于稳定,此时该特性分支就会成为核心能力分支。
最佳实践讲解
本节主要来解析每一个模块中,应该放些什么,每一层的依赖如何处理等。
分层模块
适配器模块
本层定位是系统入口,所有的业务起点,都应该在这里。
比如:controller接口、rpc接口实现、mq消费者实现等。
本层依赖:
内部依赖:核心领域模块,client模快、common模块。
三方依赖:核心框架、rpc和mq等必要框架、基础组件。
禁止依赖:业务模块、基建模块
特殊说明:
本层的模型定义在本层内、或者取自client。
本层调用领域层之前,需要做好模型转换,将模型转换为领域层里面定义的模型,然后再定义,这层的转换器应该写在这层。
业务模块
本层定位为核心业务逻辑处理,所有的业务处理都应在这里。
比如:业务流程判断走向、业务逻辑判断、根据不同的数据做不同的处理、调用数据库接口、调用mq接口、和外部交互的逻辑等。
本层依赖:核心领域模块、common模块。
三方依赖:核心框架
禁止依赖:适配器模块、基建模块、client模块、任何非必要三方依赖
特殊说明:
本层不需要做任何类型转换,进来就是自己所需要的模型。
本层不允许引入其他模块的模型,这层的模型都应该非常稳定,不能有三方注解(比如rpc注解、fastjson注解、mybatis注解、mq注解等)
基建模块
本层定位为防腐基础设施,所有和外部的交互都应该在这里,这里应该为单条线的末端。
比如:从数据库中获取数据的最终实现,从缓存中获取数据的最终实现,发送mq的实际实现,调用rpc接口的最终实现等。
本层依赖:核心领域模块、common模块。
三方依赖:核心框架、外部sdk、外部client模块、任何三方所需要的依赖。
禁止依赖:适配器模块、client模块、业务模块
特殊说明:
本层的模型可以用到领域模型或者三方交互模型。进来都是领域模型,根据需求转换为对应的三方接口模型。
处理完如果有数据透出,必须要使用类型转换器,不可把本层模型透出,也不能把三方接口模型定义到领域模块中。
领域模块
本层定位是不同模块的轴承,所有的核心交互接口定义及数据模型定义都在这里,这里联通各个模块。
比如:核心业务处理的接口(DomainService),基础建设中的能力透出接口(InnerService/Gateway)。
本层依赖:common模块
三方依赖:无
禁止依赖:几乎不允许依赖任何内容
client模块
本层定位是本系统对外提供的能力点透出。
比如:对外提供的rpc接口,对外提供的mq模型等。
本层依赖:无
三方依赖:无
禁止依赖:几乎不允许依赖任何内容
common模块
本层是不同模块之间通用的工具内容。
比如:字符串处理工具,各种util类,各种通用处理能力。
本层依赖:无
三方依赖:非常稳定的三方工具包(原则上需要自己重写一套),几乎不能引用其他三方依赖。
start模块:
应用的启动模块,定义启动器及启动所必须得一些内容。
比如:spring boot启动器,启动配置,项目中的一些配置。
本层依赖:所有的模块
三方依赖:基础框架依赖
ddd框架实践
一套最基础的增删改查实践
╔ adapter
║ ╟ java
║ ╟ ╟ RpcXXXServiceImpl.java
║ ╟ ╟ XXXController.java
║ ╟ ╟ XXXConsumer.java
║ ╟ resource
║ ╚ pom.xml
╟ biz
║ ╟ java
║ ╟ ╟ XXXDomainServiceImpl.java
║ ╚ pom.xml
╟ infra
║ ╟ java
║ ╟ ╟ XXXConverter.java
║ ╟ ╟ XXXClient.java
║ ╟ ╟ XXXInnerServiceImpl.java
║ ╟ ╟ XXXDO.java
║ ╟ resource
║ ╟ ╟ mapper
║ ╟ ╟ ╟ xxxMapper.xml
║ ╚ pom.xml
╟ domain
║ ╟ java
║ ╟ ╟ XXXDomainService.java
║ ╟ ╟ XXX.java
║ ╚ pom.xml
╟ client
║ ╟ java
║ ╟ ╟ RpcXXXService.java
║ ╟ ╟ XXXDTO.java
║ ╚ pom.xml
╟ common
║ ╟ java
║ ╟ ╟ XXXUtil.java
║ ╚ pom.xml
╟ start
║ ╟ java
║ ╟ ╟ Application.java
║ ╟ ╟ config
║ ╟ ╟ ╟ Config.java
║ ╟ resource
║ ╟ ╟ application.properties
║ ╟ ╟ META-INF
║ ╚ pom.xml
╚ pom.xml
实际demo
TODO…
相关文章:
DDD架构实践
ddd架构浅析 背景介绍 什么是ddd架构,是以ddd思想为参考,做出一份符合ddd思想的框架。 随着技术的迭代升级,越来越多的瓶颈暴露出来,性能瓶颈,系统复杂度瓶颈,这些都逐一被迭代出的技术产物解决。最终的…...
Bert-vits2-v2.2新版本本地训练推理整合包(原神八重神子英文模型miko)
近日,Bert-vits2-v2.2如约更新,该新版本v2.2主要把Emotion 模型换用CLAP多模态模型,推理支持输入text prompt提示词和audio prompt提示语音来进行引导风格化合成,让推理音色更具情感特色,并且推出了新的预处理webuI&am…...
认识缓存,一文读懂Cookie,Session缓存机制。
🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。 🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。 🎉欢迎 👍点赞✍评论…...
关于react native项目中使用react-native-wechat-lib@3.0.4
关于react native项目中使用react-native-wechat-lib3.0.4 插件官网安装依赖包(Android和iOS下载插件完成后记得更新依赖,)Android中配置1.在项目文件夹下面创建文件夹wxapi(如上图)2.在文件MainApplication.java中如下…...
【LeetCode刷题笔记(8-1)】【Python】【接雨水】【动态规划】【困难】
文章目录 引言接雨水题目描述提示 解决方案1:【动态规划】结束语 接雨水 引言 编写通过所有测试案例的代码并不简单,通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例,但如果不了解代码背后的思考过程,那么这些代…...
pycharm通过ssh连接远程服务器的docker容器进行运行和调试代码
pycharm连接远程服务器的docker容器通常有两种方法: 第一种:pycharm通过ssh连接已在运行中的docker容器 第二种:pycharm连接docker镜像,pycharm运行代码再自动创建容器 第一种方法比较通用简单,作者比较推崇。 条件…...
Chrome2023新版收藏栏UI改回旧版
版本 120.0.6099.109(正式版本)Chrome浏览器菜单新版、旧版的差异 想要将书签、功能内容改回旧版的朋友可以网址栏输入:「chrome://flags」,接着搜寻「Chrome Refresh 2023」。 最后将 Chrome Refresh 2023、Chrome Refresh 2023…...
WebSocket与JavaScript:实现实时获取位置
一、WebSocket介绍 WebSocket是一种在单个TCP连接上进行全双工通信的协议。与传统的HTTP请求相比,WebSocket能够在服务器和客户端之间建立持久连接,实现实时数据传输。WebSocket提供了较低的延迟和高效的数据传输。在实时舆情监测中,它能够实…...
一种解决Qt5发布release文件引发的无法定位程序输入点错误的方法
目录 本地环境问题描述分析解决方案 本地环境 本文将不会解释如何利用Qt5编译生成release类型的可执行文件以及如何利用windeployqt生成可执行的依赖库,请自行百度。 环境值操作系统Windows 10 专业版(22H2)Qt版本Qt 5.15.2Qt Creator版本5.0…...
UE4/UE5 日志插件(基于spdlog)
1 解决问题 对于高频日志序列化到本地的需求,spdlog肯定完美满足。 源码地址:https://github.com/gabime/spdlog 博主下载的版本为 spdlog-1.12.0,各位大佬可以根绝自己爱好选择。 2 过程介绍 大概目录: SpdlogLibC目录下是对…...
微信小程序ios中非cover组件点击重复触发地图tap事件
现象: map中使用view组件的click事件会重复触发地图的tap组件,只在ios上出现 <map id"maps" style"width: 100vw;height: 100vh;" :latitude"latitude" :longitude"longitude":markers"markers"…...
7.26 SpringBoot项目实战【还书】
文章目录 前言一、编写控制器二、编写服务层三、Git提交前言 本文是项目实战 业务接口 的最后一篇,上文 曾说过【还书】的 入口是【我的借阅记录】,因为【还书】是基于一次借阅记录而言,另外在4.2 数据库设计 曾分析过【还书】的业务场景,需要执行两步操作: 更新【借阅记…...
Golang中使用errors返回调用堆栈信息
Golang的errors包返回堆栈信息 标准库errors提供了处理错误的方法。比如常用的 func New(text string) error 用该方法处理错误信息,就只会输出自定义的 text 到控制台或者日志文件,没有其它辅助排查的信息输出,所以常规我们就只能根据 te…...
Web前端-HTML(常用标签)
文章目录 1. HTML常用标签1.1 排版标签1)标题标签h (熟记)2)段落标签p ( 熟记)3)水平线标签hr(认识)4)换行标签br (熟记)5)div 和 span标签(重点)6)排版标签总结 1.2 标签属性1.3 图像标签img (重点)1.4 链…...
一 OpenCV中的数据类型
1. cv::Mat 2. cv::Point 主要用来表示二维点,也有表示三维点的模板类型; cv::Point p(int, int) 最常用 ① cv::Point_<T> ② cv::Point2i cv::Point_<int> ③ cv::Point2f cv::Point_<float> ④ cv::Point2d …...
59. 螺旋矩阵 II(java实现,史上最详细教程,想学会的进!!!)
今天来分享一下螺旋矩阵的解题思路及代码的实现。 题目描述如下: 首先拿到这道题,首先不要慌张,我们来仔细分析一下会发现并没有那么难。 首先看下边界的元素是1、2、3递增的,那么我们也许可以根据这一点先把边界的元素一个一个给…...
vue 将后端返回的二进制流进行处理并实现下载
什么是二进制流文件? 二进制文件是一种计算机文件格式,它的数据以二进制形式存储,与文本文件不同。二进制文件可以包含任意类型的数据,例如图像、音频、视频、可执行文件、压缩文件等,而文本文件则仅仅包含 ASCII 码或…...
PyCharm连接远程服务器
要求:PyCharm专业版才支持远程服务 一、创建远程连接 先建立本地与远程服务器之间的SSH连接 1、配置连接 2、建立SSH连接,选择文件传输协议 SFTP 3、设置服务器名(可以随意命名) 4、配置 SSH连接 点击 172.18.1.202 配置…...
使用Qt制作网易云播放器的歌曲排行界面
!!!直接上图!!! !!!直接上图!!! !!!直接上图!!! 网易云排行榜…...
【.NET Core】特性(Attribute)详解
【.NET Core】特性(Attribute)详解 文章目录 【.NET Core】特性(Attribute)详解一、概述二、编写自定义属性2.1 自定义特性的主要步骤2.2 应用AttributeUsageAttributeAttributeTargets 成员Inherited属性AllowMultiple属性 三、声…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
