项目架构MVC,DDD学习
写在前面
本文一起看下项目架构DDD,MVC相关的内容。
1:MVC
不管我们做什么项目,自己想想其实只是做了三件事,如下:
其实,这三件事完全在一个类中做完也可以可以正常把项目完成的,就像下面这样:
@RequestMapping("/xxx")
public void XxxHttp {private String userId;private String username;public Response queryUerById(String userId) {queryFromDb(userId);Response res = assembleRes();return res;}// 查询数据public void queryFromDb(String userId) {// 查询数据库的操作,获取username等信息}// 组装响应public Response assembleRes() {}
}
试想下,如果所有的东西全部都塞到一个类里去,那么这个后期项目的可维护性和可扩展性几乎为0。针对这个问题,系统架构就应运而生了,而MVC就是系统架构的一种,当然DDD也是,本文后边部分也会学习到。这里,先来只看MVC相关的内容,MVC通过分层的思想来解耦程序,增加可维护性和可扩展性,把不同的属性分配到不同类型的对象中,把方法进行更细粒度的划分,大概如下图:
对每部分功能进行细分后,MVC同样会将其划分到不同的层,即文件夹中,从而使得结构更加的清晰,基本上如下图:
为了对MVC有一个更加清晰的认识,以springboot的方式来看一个实际的项目例子:
为了方便学习,我录制了一个视频放在这里:
项目架构MVC
视频中MVC系统架构用到的关键层所对应的文件夹都已经创建完毕了。详细的参考下源码 。
再来看下最后的效果:
- 通过单元测试测试
- 访问接口
这样,一个典型的MVC项目我们就完成了!
接着继续来看DDD项目又是咋回事。
2:DDD
源码 。
DDD全称,domain-driven design,即领域驱动设计,是一种系统架构设计的思想,根据这种思想我们可以通过不同的模块来组织我们的项目结构,其中一种可能的组织方式如下:
注意:默认使用spring boot。
app:定义项目全局内容,如启动类,AOP配置,dockerfile,启动脚本等。
types:定义公用的常量类,枚举类,响应结果类,带分页的类等。
api:定义需要对外暴漏的类和接口,如dubbbo的服务描述接口,以及请求和响应中需要用到的类。
domain:最关键的模块,定义出不同业务模块的仓储接口,服务类(给出具体的实现),model(聚合类,entity,valobjs)。
infrastructure:对domain的仓储接口给出具体的实现,以及定义映射数据库表的PO类。
trigger:触发模块,作为触发程序执行的模块,如接收http接口的controller,接收rpc请求的provider,消费消息的消费者,以及自动定时执行的job等。
本文也会按照这种组织方式来共同进行学习。
2.1:app
定义app之前先来看下app模块中写啥东西:
全局的配置可以写在这里,比如:
1:项目级的AOP配置
2:项目级的config配置(比如配置redis的序列化方式等)
3:打包镜像,启动脚本(start.sh等)
2.2:types
定义types之前来看下types模块中写啥东西:
来定义一些通用的类型,比如常量类Constants,接口响应用到的公共对象,
比如定义了响应状态码和data数据ReponseEntity(当然不一定非得是这个名字)对象。
当然其他的只要是各个业务模块都可用用到的公共的类都可以定义在这里,但注意一定要是公用的哟!!!
- 一个可能的常量类
// 你们都甭继承我!!!
public final class Constants {public static final String ERROR_MSG = "你干哈,都给我搞出错了!!!";
}
- 一个可能的封装相应信息的公共类
@Builder
public class ResEntity<T> {private T data;private int code;
}
其他的可根据具体情况定义在这里。
2.3:api
定义api之前来看下api模块中写啥东西:
如外部需要用到的rpc描述文件(需要打成jar包,提供出来),以及描述文件中需要用到的相关类,
如入参以及返回的对象等。需要对外暴漏的类的和接口(如dubbo服务描述接口)
因此,一般该模块是提供出去给外部使用的,当然自己也会使用到。
- 定义可能的dubbo接口
/*** dubbo的某个对外服务接口*/
public interface DubboXxxInterface {DubboReqObj querySth(DubboReqObj dubboReqObj);
}
- 入参和出参可能的用到的类
/*** dubbo的某入参对象(使用者需要知道)*/
public class DubboReqObj {
}/*** dubbo的某出参对象(使用者需要知道)*/
public class DubboResObj {
}
2.4:domain
定义domain之前来看下domain模块中写啥东西:
首先按照业务模块,一个业务模块一个文件夹。在每个文件夹下分别定义的信息如下:
model:同MVC系统架构的domain层定义的内容aggregates:组合valobjs,因为某个业务可能需要多种VO组合在一起才能满足需求,当然也可以聚合entity,所以该类对象就是用来聚合entity和valobjs,从而满足业务对数据需求的的一类对象entity:一般和数据库实体对象是1v1的关系,但是相比与数据库实体PO,只包含其中业务相关的信息,比如id啊,创建时间啊,删除状态啊这些是不需要的,po的一些有限状态的字段在entity中使用枚举来表示,po的逗号分隔的信息在entity使用list形式来表示,总是呢,其实就是将po转换为更加符合业务需要的方式,这种更加符合业务需要的方式就是entity了,比如有PO,信息是private int id, private int status(1代表xxx 2代表xxx), private varchar hobbies(逗号分割),转换为entity就是 private StatusEnum status(1代表xxx 2代表xxx), private List hobbiesList,大概这种!哦,对,entity还有另一个作用对po做防腐,意思就是不要你随便糟蹋,嚯嚯PO同学。valobjs:应该就是VO,给UI使用的
repository: 定义数据库操作的接口,infrastructure会通过依赖倒置的方式来提供具体的实现
service:具体服务代码的实现,同MVC系统架构的service,不过只定义本业务模块的相关服务类,相比于MVC的service范围更小,即是其子集,需要注意名字不一定非得是xxxService,也可以是xxxEgine,xxxFilter,xxxProcessor,xxxHandler等这种,当然为了能够更加清晰也可以再创建对应的engine,filter等子文件夹
- 可能的model信息(聚合对象,entity,valobjs)
- 可能的仓储接口
/*** 用户仓库类*/
public interface IUserRepository {UserAggregates queryUserByIds(List<String> userIdList);
}
- 具体服务类
public interface IUserService {
}public class UserServiceImpl implements IUserService {// 通过spring容器注入在infrastructure模块中提供的仓库具体实现@Resourceprivate IUserRepository userRepository;
}
以上private IUserRepository userRepository
我们会在infrastructure基础设施模块提供具体实现,并交给spring管理。
2.6:infrastructure
定义infrastructure之前来看下infrastructure模块中写啥东西:
1:基于domain层提供数据库操作的具体实现(在domain层中我们定义了数据库操作的接口了不是),与domain是一种依赖倒置的关系
2:和数据库表对应的PO也在这里写
3:对domain定义的仓库接口提供具体实现,因此需要依赖于domain模块,但注意domain模块不能依赖于infrastructure模块,但domain又需要具体的数据库接口实现的dao,咋办呢?通过spring 容器注入即可。
- user表PO
/*** 数据库表user的映射(注意,需要做防腐处理,避免最重要的数据查询出现问题!!!)*/
public class UserPO {/** 用户ID */private Long id;/** 用户名称 */private String userId;/** 用户昵称 */private String userNickname;/** 用户头像 */private String userHead;/** 账号密码 */private String userPassword;/** 创建时间 */private Date createTime;/** 修改时间 */private Date updateTime;}
- dao
@Mapper
public interface IUserDao {List<UserPO> queryUserList();}
- 仓储实现
@Component
public class UserRepository implements IUserRepository {@Resourceprivate IUserDao userDao;@Overridepublic UserAggregates queryUserByIds(List<String> userIdList) {List<UserPO> userPOS = userDao.queryUserList();UserAggregates userAggregates = new UserAggregates();// TODO userPOS -> userAggregates 略!for (UserPO userPO : userPOS) {System.out.println(userPO);}return userAggregates;}
}
2.7:trigger
定义trigger之前来看下trigger模块中写啥东西:
写触发程序执行的相关内容,如
1:接收http请求的controller
2:接收mq消息的消费者
3:rpc调用的server(如dubbo server)
4:定时任务(如自己定义的timer,或者是等待xxx-job等分布式任务调度框架执行的代码)
5;其他等
- http
@RestController
public class Controller {// 注入domain的service实现类就可以编写具体的处理逻辑了,这和常规的mvc就一样了!!!@Resourceprivate IUserService userService;@RequestMapping("/user")public ResEntity<String> queryUser() {// TODO 使用 userService写具体的逻辑,略!!!return ResEntity.<String>builder().data("cccccc").build();}
}
- rpc
/*** 某dubbo的provider*/
public class XxxDubboProvider {
}
- timer
/*** 某定时执行的任务*/
public class SomeXxxJob {
}
为了更好的帮助你理解DDD的内容,我录制了一个视频,放在这里:
项目架构DDD,手把手从零搭建一个落地的DDD项目,没有枯燥的理论,就是写代码!!!
写在后面
参考文章列表
架构的本质之MVC架构 —— Java简明教程,一套简单、清晰、明了的Java学习路线资料!!! 。
相关文章:

项目架构MVC,DDD学习
写在前面 本文一起看下项目架构DDD,MVC相关的内容。 1:MVC 不管我们做什么项目,自己想想其实只是做了三件事,如下: 其实,这三件事完全在一个类中做完也可以可以正常把项目完成的,就像下面这…...
SQLite的PRAGMA 声明
PRAGMA 语句是特定于 SQLite 的 SQL 扩展,用于 修改 SQLite 库的操作或查询 SQLite 库 内部(非表)数据。PRAGMA声明使用相同的 接口作为其他 SQLite 命令(例如 SELECT、INSERT)但 在以下重要方面有所不同: …...

使用ArrayList.removeAll(List list)导致的机器重启
背景 先说一下背景,博主所在的业务组有一个核心系统,需要同步两个不同数据源给过来的数据到redis中,但是每次同步之前需要过滤掉一部分数据,只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…...
如何在项目中使用uni-ui组件库
1、安装uni-ui npm i dcloudio/uni-ui 2、组件自动引用 配置easycom 使用 npm 安装好 uni-ui 之后,需要配置 easycom 规则,让 npm 安装的组件支持 easycom 打开项目根目录下的 pages.json 并添加 easycom 节点: // pages.json {"e…...
redis的过期策略和内存淘汰机制(redis篇)
分享并学习一下redis的过期策略和内存淘汰机制 在平时的工作或者学习中,即便自己没有实打实的用过redis。但是能有对这方面的思考,再结合一些实际场景和理论,那么我相信自己或者你都会越来越厉害的。 首先,我们需要认清为啥redis要…...
Java中Runnable和Callable有什么不同?(企业真题)
Java中Runnable和Callable有什么不同? 与之前的方式的对比:与Runnable方式的对比的好处 call()可以有返回值,更灵活 call()可以使用throws的方式处理异常,更灵活 Callable使用了泛型参数,可以指明具体的call()的返回值…...

图机器学习导论
图:描述关系数据的通用语言,起源于哥尼斯堡七桥问题 传统的机器学习:数据样本之间独立同分布,简单拟合数据边界,在传统的机器学习中,每个数据样本彼此无关。传统的神经网络,只能处理简单的表格、…...

地推网推拉新平台哪家强?一文清楚告诉你
在当今这个充满副业的时代,地推网推拉新平台的寻找与对接成为了许多人关注的焦点。那么,我们应该如何找到那些既靠谱又有潜力的拉新项目呢? 经过深入研究和全网检索,我为大家盘点了5个值得一试地推网推拉新平台。 尤其是“聚小推…...

Day:004(4) | Python爬虫:高效数据抓取的编程技术(数据解析)
XPath工具 浏览器-元素-CtrlF 浏览器-控制台- $x(表达式) Xpath helper (安装包需要科学上网) 问题 使用离线安装包 出现 程序包无效 解决方案 使用修改安装包的后缀名为 rar,解压文件到一个文件夹,再用 加载文件夹的方式安装即可 安装 python若使用…...
(80) 只出现一次的数字(81)反转字符串
文章目录 1. 每日一言2. (80) 只出现一次的数字2.1 解题思路2.2 代码 3. (81)反转字符串3.1 解题思路3.2 代码 4. 结语 1. 每日一言 生活是一场即兴表演,值得庆幸的是我们总是有所感受,并且将一直感受下去。 2. (80) 只出现一次的数字 题目链接&#x…...

基于拉格朗日分布算法的电动汽车充放电调度MATLAB程序
微❤关注“电气仔推送”获得资料(专享优惠) 程序简介 该模型主要做的是基于拉格朗日分布算法的电动汽车充放电调度模型。利用蒙特卡洛模拟法模拟出电动汽车负荷曲线,并求解出无序充电功率曲线和有序充电曲线,该模型在电动汽车个…...

【Linux 学习】进程优先级和命令行参数!
1. 什么是优先级? 指定进程获取某种资源(CPU)的先后顺序; Linux 中优先级数字越小,优先级越高; 1.1 优先级和权限的区别? 权限 : 能不能做 优先级: 已经能了,但是获…...
Git删除未跟踪的文件Untracked files
在 Git 中,要删除未跟踪的文件(Untracked files),你可以使用 git clean 命令。请注意,这个命令会从你的工作目录中永久删除这些文件,因此在执行之前请确保你不再需要这些文件或已经妥善备份。 以下是如何使…...
S7-1200PLC控制V90伺服通过FB284实现位置控制的方法
S7-1200PLC控制V90伺服通过FB284实现位置控制的方法 通过西门子报文111和FB284功能块 在V-ASSISTANT中将V90 PN设置控制模式为"基本位置控制(EPOS)" V90 PN与PLC采用PROFINET RT通信方式并使用西门子报文111。 在博途中V90 PN的设备视图中更改报文为:报文111 安装…...

2024年阿里云优惠券领取和使用方法
阿里云优惠代金券领取入口,阿里云服务器优惠代金券、域名代金券,在领券中心可以领取当前最新可用的满减代金券,阿里云百科aliyunbaike.com分享阿里云服务器代金券、领券中心、域名代金券领取、代金券查询及使用方法: 阿里云优惠券…...

工业项目中你连PLM系统都没见过?
什么是 PLM 软件? PLM 软件是用于管理全球供应链中产品或服务全生命周期环节的解决方案。它包括从物料、零部件、产品、文档、规定、工程变更单到质量工作流的数据管理。 PLM 的发展历史 从最初的产品设计管理到如今的数字化转型和智能化生产,PLM 在不断…...

【QT入门】 Qt自定义控件与样式设计之QPushButton实现鼠标悬浮按钮弹出对话框
往期回顾: 【QT入门】 Qt自定义控件与样式设计之qss选择器-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QLineEdit的qss使用-CSDN博客 【QT入门】Qt自定义控件与样式设计之QPushButton常用qss-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QPushButton实现鼠标悬…...

C盘变红怎么办?免费的系统C盘清理方法,C盘空间占用克星
百夫说:分享免费又好用的工具,是一件快乐的事情。 正文: 起因:C盘报警,系统变慢 立即下载XX系统清理大师,搜索出垃圾数据近30G,开心的点击“一键清理”,结果提示要收费:…...
简述VPS 与 Apache 搭建网站方式对比:新手科普指南
在互联网时代,拥有一个网站对于个人、企业以及组织来说已经成为了必备的一项资源。然而,对于新手来说,如何搭建一个网站可能是一个挑战。在这篇文章中,我将探讨两种常见的搭建网站的方式:使用虚拟专用服务器࿰…...
js获取年月份
一、date 如何使用、如何获取年月日时分秒、时间戳、如何获取指定日期的时间戳或周几 1..Date 对象用于处理日期和时间。 创建 Date 对象的语法: var myDatenew Date() 获取年月日时分秒: // 格式化日对象 const getNowDate () > {let date new …...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...