项目架构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 …...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
