项目架构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学习路线资料!!! 。

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

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

相关文章

【MacBook系统homebrew镜像记录】

安装 使用Homebrew 国内源安装脚本,贼方便&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"切换至清华大学镜像源&#xff1a; 命令合并&#xff1a; 分别切换了 brew.git、 homebrew-core.git、 homebrew-…

逆向案例十二——看准网企业信息json格式的信息

网址&#xff1a;【全国公司排行|排名榜单|哪家好】-看准网 打开开发者工具——刷新——网络——XHR——下滑页面加载新的页面——找到数据包 发现参数加密&#xff0c;返回的数据也进行了加密 按关键字在下方搜索 kiv进入第一个js文件 ctrlf打开文件里面的搜索框继续搜kiv找到…

Java 面试宝典:Redis 的线程模型是怎么样的?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站&#xff1a;https://www.skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 Redis 的线程模型其实是分两块的&#xff1a; Redis 6.0 …

前端开发语言有那些?

前端开发语言有那些&#xff1f; 1、html 超文本标记语言&#xff1a;构建前端网页的基本结构&#xff0c;就象人的骨架一样。 2、css 层叠样式表&#xff1a;控制网页的样式和布局&#xff0c;就象人需要穿各种服式展现不同风采。 3、javascript 简称 JS 动态脚本语言&#x…

阿里面试总结

ThreadLocal 线程变量存放在当前线程变量中&#xff0c;线程上下文中&#xff0c;set将变量添加到threadLocals变量中 Thread类中定义了两个ThreadLocalMap类型变量threadLocals、inheritableThreadLocals用来存储当前操作的ThreadLocal的引用及变量对象&#xff0c;把当前线程…

括号串(Deque)

题目 import java.util.Deque; import java.util.LinkedList; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();sc.nextLine();char[] c sc.nextLine().toCharArray();Deque…

Flutter第六弹 基础列表ListView

目标&#xff1a; 1&#xff09;Flutter有哪些常用的列表组建 2&#xff09;怎么定制列表项Item&#xff1f; 一、ListView简介 使用标准的 ListView 构造方法非常适合只有少量数据的列表。我们还将使用内置的 ListTile widget 来给我们的条目提供可视化结构。ListView支持…

性能分析--内存知识

内存相关知识 计算机中与CPU进行数据交换的桥梁。内存的速度&#xff0c;比CPU的速度要慢很多。比磁盘速度要快很多。内存中存放数据&#xff0c;一旦断电就会消失。linux系统的 /proc路径下的文件&#xff0c;都是内存文件。内存大小&#xff0c;一般 是GB为单位。 现在都操作…

WebKit是什么?

WebKit是一个开源的浏览器引擎&#xff0c;它用于呈现网页内容在许多现代浏览器中&#xff0c;包括Safari浏览器、iOS内置浏览器、以及一些其他浏览器如Google Chrome的早期版本。以下是一些关于WebKit的重要信息&#xff1a; 起源和发展&#xff1a;WebKit最初是由苹果公司为其…

K8s学习四(资源调度_1)

资源调度 发现对Pod操作不方便&#xff0c;不能直接操作&#xff0c;而且不能直接编辑&#xff0c;需要对原来的配置文件进行操作&#xff0c;而且需要删除之后再创建Pod&#xff0c;不方便&#xff0c;更多是通过控制器来操作。 Label和Selector 通过设置标签和选择器来确定…

Python爬虫:为什么你爬取不到网页数据

目录 前言 一、网络请求被拒绝 二、数据是通过JavaScript加载的 三、需要进行登录 四、网站反爬虫策略 五、网站结构变更 总结 前言 作为一名开发者&#xff0c;使用Python编写爬虫程序是一项常见的任务。爬虫程序的目的是收集互联网上的数据&#xff0c;并将其保存或使…

解决IDEA 控制台中文乱码

运行某个项目时IntelliJ IDEA 控制台中文乱码&#xff0c;但其他的项目是正常的。接口文档也显示乱码&#xff1a; 一、修改 IntelliJ IDEA 全局编码、项目编码、属性文件编码 上方导航栏“File→Settings…”进入配置页面&#xff0c;在“Editor”中下滑找到“File Encodings…

LeetCode 面试题 02.07.链表相交(判断两个结点是否相同)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&#x…

内外网数据交换发展进程:安全与便捷并行

随着信息化的不断推进&#xff0c;医院、党政以及企业的内外网数据交换正成为日益关注的焦点。在保障数据安全的前提下&#xff0c;需要寻求一种既安全可靠又操作便捷的数据传输方式。本文将探讨内外网数据交换发展进程&#xff0c;分析各种传输方式的优缺点&#xff0c;以及它…

麒麟系统ARM安装rabbitmq

简单记录下&#xff0c;信创服务器&#xff1a;麒麟系统&#xff0c;安装rabbitmq的踩坑记录。 本文章参考了很多大佬文章&#xff0c;我整理后提供。 一、安装基础依赖 yum -y install make gcc gcc-c kernel-devel m4 ncurses-devel openssl-devel unixODBC-devel 二、下载…

k8s资源监控_bitnami metrics-server v0(1),2024一位Linux运维中级程序员的跳槽面经

错误3 也有可能会遇到以下错误&#xff0c;按照下面提示解决 Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io) 如果metrics-server正常启动&#xff0c;没有错误&#xff0c;应该就是网络问题。修改…

花一分钟简单认识 CSS 中的规则 —— 级联层 @layer

layer 简介&#xff1a; 声明级联层时&#xff0c;越靠后优先级越高。不属于任何级联层的样式&#xff0c;将自成一层匿名级联层&#xff0c;并置于所有层之后 —— 级别最高。 用法一&#xff1a;在同一文件中 layer base, special; layer special {/* 优先 */li { color: …

Python学习笔记——heapq

堆排序 思路 堆排序思路是&#xff1a; 将数组以二叉树的形式分析&#xff0c;令根节点索引值为0&#xff0c;索引值为index的节点&#xff0c;子节点索引值分别为index*21、index*22&#xff1b;对二叉树进行维护&#xff0c;使得每个非叶子节点的值&#xff0c;都大于或者…

2024-4-7 QT day1作业

myWidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\张谦\\Desktop\\pictrue\\qq.png"));//设…

git bash上传文件至github仓库

Linux运维工具-ywtool 目录 一.访问github二.新建仓库1.点击自己头像2.选择"your repositories"3.点击"New"4.创建新仓库 三.通过git bash软件上传文件1.提示2.打开git bash软件3.切换到本地仓库目录4.配置github的用户名和邮箱信息5.生成SSH Key6.github添…