
















最近闲来无事,正好前不久将以前到处复制粘贴的代码整理成了一套完整的java 项目模板 ,所以就趁此机会梳理一下个人一直以来的分层逻辑,温故而知新,同时希望可以给后来者一些指导方向,免得后来的初学者像我当初一样对着乱七八糟的项目焦头烂额,完全不知道对方每个包是如何定义的,也不知道如何有效快速地新建一个项目。
本文全为笔者个人的经验之谈,在某些地方可能存在大量的主观建议,如有不同意见,欢迎与笔者联系。
首先接触后端项目绕不开的就是经典的 MVC,何为 MVC?MVC,即 Model-View-Controller,View 是展示给用户看的界面;Model 是后端编写的模型;Controller 则是连接 View 和 Model 的桥梁,接收 View 的请求发送给 Model,同时收到 Model 处理的结果返回给 View。理解了这三个概念则我们很容易得到一个结论,后端首先需要一个 controller 层来定义与前端交互的接口,其次需要一个 model 层来存储数据库模型,另外每个模型同时需要一些增删改查和一些其他的处理操作,这些逻辑则需要放在 service 层里面,这是后端项目的基础逻辑,下面将详细讲述每个分层的内容。
在 controller 层中,我们一般是需要根据REST 风格 定义 api,简要地说就是以 http 方法来区分对资源的操作类型,GET-查询,POST-新增,PUT-修改,DELTE-删除(不过事实上,笔者写代码这么久很少见到严格遵守 REST 风格的接口,一般都是查询使用 GET,其余均使用 POST)。
mvc 中的 model 事实上我认为是一个比较大的概念,包含了 controller 后面的所有内容,所以下面我将对这块儿内容进行详细描述。
首先可以肯定的是我们在项目初期一定是先定义好的数据库中的各个表,所有逻辑都是围绕着这些表展开,如此则引出了我们需要一个层用来存储数据表的模型,笔者习惯称之为 entity,同时也需要一个层用来与数据库交互,用来直接对数据库操作(一般是直接写 sql),笔者习惯称之为 mapper。
当与数据库的操作定义好之后,我们则需要开始写复杂的业务逻辑了,这些逻辑则需要在 service 层中完成,service 层中不会直接操作数据库,而是调用 mapper 层中的方法。
从数据库到业务逻辑的部分已经完成,接下来则是 controller 和 service 的交互,这里一般上是会定义一层 dto(data transfer object),用来传输 service 和 service 以及 controllerr 和 service 中的数据。而 controller 的接口中接收以及返回的数据则一般会定义在 vo(value object)中。
上面描述了如何将 mvc 的分层思想提现在项目开发中,接下来我将分别以 java 和 go 举例,聊一聊实际项目我是如何实现分层的。
java 的目录结构基本都体现在这个项目模板
里了 (这是jdk8的,最近很多项目都升级到jdk17甚至jdk21了,呜呜呜,依赖版本匹配好麻烦,不想升级),下面我详细解释一下每个包的作用和输入输出,以及一些可能会出现但是我没有使用的结构。
数据模型:
数据库操作:
业务操作:
杂项:
因为 go 的 web 开发中没有 spring 这种大一统的开发框架,所以这导致 go 的目录结构更加扑朔迷离,每个 web 框架都有自己的特性,不过总体上还是可以参考 java 的一套逻辑进行开发,毕竟殊途同归,下面则是以hertz 框架为例讲些常用的一些目录结构。
因为 hertz 可以使用 thrift 生成一些基本的代码,所以笔者给一个简单的 thrift 文件作为例子:
namespace go test_service
struct BaseResp {
1: i32 code
2: string msg
}
struct HelloReq {
1: string text
}
struct HelloResp {
1: i32 code
2: string msg
3: HelloResult data
}
struct HelloResult {
1: string result
}
service HelloHandler {
HelloResp Hello(1: HelloReq req) (api.post="/iapi/test/hello")
}
在使用 hz 命令生成代码后我们会得到一个基本的目录结构,接下来我们对其进行补充,完整的目录结构应该如下所述:
最近写项目深入用了一下,发现字节的那一系列工具似乎有些问题(比如sonic 为了性能大量使用了汇编,大概可能因此无法在riscv机器上正常运行;hertz和thriftgo的版本匹配有些问题,导致生成的代码有些问题。。。),所以笔者还是回归了最简单的Gin 框架进行开发,下面是笔者目前使用Gin开发的目录框架:
map_util.go中,而一个监控系统cpu和gpu的工具可以在pkg下面定义IMonitor接口以及CPUMonitor和GPUMonitor的实现)说到项目分层就不得不提一嘴 DDD(Domain-Driven Design),事实上我一直搞不懂也不敢妄言了解 DDD,看过大量关于 DDD 的博客、视频和实际项目,我的总结是:“每个人都有属于自己的 DDD”。犹记得之前看过一个腾讯的论坛,前面几个大佬讲解时评论区一片叫好,而到最后一个大佬讲解 DDD 时,“这个人技术水平有点低”、“他根本不懂 DDD”等类似的言论充斥在评论区。既然有着几年架构经验的腾讯大佬都被评价为“根本不懂”,那我这一个无名小卒大概也只能是说一下自己的浅薄认知了,如有理解不对的地方还望有大佬不吝赐教。(叠甲完毕)
个人认为 DDD 是为了应对传统 MVC 架构中一个服务承担了太多逻辑而出现的,当一个服务承担了多张表相关的业务逻辑时,代码将会膨胀到很难维护的地步,而为了解决此问题,DDD 中将几个相关较大的表(也就是一个域)相关的业务逻辑拆出来独立成一个目录,这样在修改时只需要关注对应域的代码即可,大大降低了维护成本。而相同的逻辑,如统一网关,通用的工具和配置类等也分别定义在一个域中,这样也保证了代码的可复用性。简单来说就是提取出可复用的,将原本的一个大 MVC 拆解成多个小 MVC。(实际上,我并不理解这和微服务有什么区别?)
DDD 中还有个充血模型的概念,但笔者认为这并不是一个好的思路,一个数据模型的类应当保证他的纯洁性,他最多应该只能有字段的定义、构造器、getter、setter 方法,以及一些类似于 from、to、of 等类型转换方法,不应该过分涉及业务逻辑,尤其是对数据库的操作逻辑。
至于 DDD 的分层,这就完全按照每个人自己理解的 DDD 进行分层了,至少到目前为止,笔者还没见到哪两位不同的作者写出来的所谓的 DDD 项目有着相似的分层结构。笔者虽然也接触过一些或许是 DDD 的项目(“或许是”是因为笔者问了身边一些大佬,有的人说这是 DDD,而有的人说不是),但并不认为他们的结构具备参考价值,也就不在这献丑了。
关于DDD,最近又看了一些文章,大概理解DDD像搭积木一样,每个实体类里面实现自己的逻辑,例如User类里面实现密码校验、Account类里面实现转账,service里面只是像搭积木一样把这些逻辑拼起来,如果需要修改某个业务逻辑时,只需要修改对应的实体类即可,而不需要修改service层。
(关于DDD的内容将持续更新中。。。)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。