当前位置: 首页 > news >正文

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实体定义等。

这些内容是切面与系统存在的,需要提供给外部引用,应该单独出来,避免把自己的核心内容给外部使用。

优势:

  1. 避免由于外部使用不当,导致自己内部的实体变更,让交互不稳定。
  2. 避免由于提供依赖太大,导致对方系统的负担
  3. 避免核心能力让对方引用,增加安全风险

遵守规则:

  1. client包中,不应有任何三方依赖,只是一个简单接口及实体的定义
  2. 不应有内部使用的包,只需要提供外部所需的即可,遵守最小可用原则
  3. 统一出参封装,所有对外提供的接口应统一封装,方便框架层面做统一处理。

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事件

现象&#xff1a; map中使用view组件的click事件会重复触发地图的tap组件&#xff0c;只在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 用该方法处理错误信息&#xff0c;就只会输出自定义的 text 到控制台或者日志文件&#xff0c;没有其它辅助排查的信息输出&#xff0c;所以常规我们就只能根据 te…...

Web前端-HTML(常用标签)

文章目录 1. HTML常用标签1.1 排版标签1&#xff09;标题标签h (熟记)2&#xff09;段落标签p ( 熟记)3&#xff09;水平线标签hr(认识)4&#xff09;换行标签br (熟记)5&#xff09;div 和 span标签(重点)6&#xff09;排版标签总结 1.2 标签属性1.3 图像标签img (重点)1.4 链…...

一 OpenCV中的数据类型

1. cv::Mat 2. cv::Point 主要用来表示二维点&#xff0c;也有表示三维点的模板类型&#xff1b; cv::Point p(int, int) 最常用 ① cv::Point_<T> ② cv::Point2i cv::Point_<int> ③ cv::Point2f cv::Point_<float> ④ cv::Point2d …...

59. 螺旋矩阵 II(java实现,史上最详细教程,想学会的进!!!)

今天来分享一下螺旋矩阵的解题思路及代码的实现。 题目描述如下&#xff1a; 首先拿到这道题&#xff0c;首先不要慌张&#xff0c;我们来仔细分析一下会发现并没有那么难。 首先看下边界的元素是1、2、3递增的&#xff0c;那么我们也许可以根据这一点先把边界的元素一个一个给…...

vue 将后端返回的二进制流进行处理并实现下载

什么是二进制流文件&#xff1f; 二进制文件是一种计算机文件格式&#xff0c;它的数据以二进制形式存储&#xff0c;与文本文件不同。二进制文件可以包含任意类型的数据&#xff0c;例如图像、音频、视频、可执行文件、压缩文件等&#xff0c;而文本文件则仅仅包含 ASCII 码或…...

PyCharm连接远程服务器

要求&#xff1a;PyCharm专业版才支持远程服务 一、创建远程连接 先建立本地与远程服务器之间的SSH连接 1、配置连接 2、建立SSH连接&#xff0c;选择文件传输协议 SFTP 3、设置服务器名&#xff08;可以随意命名&#xff09; 4、配置 SSH连接 点击 172.18.1.202 配置…...

使用Qt制作网易云播放器的歌曲排行界面

&#xff01;&#xff01;&#xff01;直接上图&#xff01;&#xff01;&#xff01; &#xff01;&#xff01;&#xff01;直接上图&#xff01;&#xff01;&#xff01; &#xff01;&#xff01;&#xff01;直接上图&#xff01;&#xff01;&#xff01; 网易云排行榜…...

【.NET Core】特性(Attribute)详解

【.NET Core】特性&#xff08;Attribute&#xff09;详解 文章目录 【.NET Core】特性&#xff08;Attribute&#xff09;详解一、概述二、编写自定义属性2.1 自定义特性的主要步骤2.2 应用AttributeUsageAttributeAttributeTargets 成员Inherited属性AllowMultiple属性 三、声…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

Python的__call__ 方法

在 Python 中&#xff0c;__call__ 是一个特殊的魔术方法&#xff08;magic method&#xff09;&#xff0c;它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时&#xff08;例如 obj()&#xff09;&#xff0c;Python 会自动调用该对象的 __call__ 方法…...