掘金 后端 ( ) • 2024-05-08 09:01

写在文章开头

近期和读者交流聊到项目规范,借着这个机会我们不妨聊聊主流Java项目是如何进行分层的。

Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

为什么需要分层

大部分人都认为Java项目的分层无非是controllerservicemapper这3层,尽管这种规约在Java web项目几乎是默认的,有时却因为团队沟通或者需求快速迭代,导致项目中会出现以下几种情况:

  1. 接口逻辑全写在controllerservice仅做透传持久层的透传。
  2. mapper层对象直接返回到controller层,直接无视service层。
  3. controller层对象不做任何转换直接一路传到mapper层。

就笔者的个人经验而言,这种情况大部分出现在有一定开发经验老码农身上,而新人接手项目时没有经过沟通,直接照猫画虎,于是这种恶习便一路传下来,进而出现如下几个问题:

  1. 代码不易扩展。
  2. 分层效果让团队难以接受。
  3. 各层职责边界不清晰,难以复用。

分层最佳实践

阿里提出的分层规范

按照阿里的规约,对应的项目会按照如下规范进行分层:

  1. 开放接口层:封装service方法对外暴露为RPC接口或者通过web封装成HTTP接口进行网关安全控制或者流量控制等。
  2. 终端显示层:各个模板渲染并显示的层级,我们可直接通俗的理解为前端渲染的页面。
  3. web层:主要对访问控制进行转发、参数校验、逻辑调用等,也就是我们常说的controller层。
  4. service:真正逻辑处理的层级,严格来说一个controller就对应一个service,之所以用逻辑都封装在service是为了方便后续封装RPC接口时,可以通过直接调用service完成扩展。
  5. manager:通用的业务处理层级,常用于第三方平台能力封装,或者中间件、缓存方案通用处理、对多mapper进行逻辑组合,总的来说manager层提供各种原子服务接口,而service层对这些原子接口进行逻辑编排。
  6. mapper层:持久层,也就是直接和数据库交互的一层。

对应的我们给出分层的图解:

分层的领域模型

明确分层后,我们也需要针对各层提出领域模型规约:

  1. DO(data object):即持久层对象,所有的数据库查询后转换为Java映射的对象都以DO结尾,该层与数据表一一对应。
  2. DTO(data transfer object):持久层完成查询之后得到的DO对象service层或者manager层就需要将其转换为DTO对外传输。
  3. VO(view object)controller层返回给视图层进行渲染的对象。
  4. query:入参对象,如果超过两个则需要封装,禁止使用Map,这是强制要求。

很明显,这种做法虽然保证了层级分明,但是为了所谓的规范却耗费开发大量精力进行对象转化,明显降低了开发的效率。

简化分层规约

所以我们对此做一个折中,得到下面这样一个开发规约图解,其交互过程为:

  1. 入参一律使用query,进行参数校验之后,由于controller或者Tservicequery传给service或者manager
  2. servicemanager在业务处理时可直接操作mapper层的DO对象进行逻辑处理。
  3. servicemanager完成逻辑后最终封装成VO返回给上层也就是controller或者Tservice层。

由此我们既实现了规范的的分层又保证了开发的效率。

常见的web架构示例

基于上述的分层理论,笔者给出一个个人比较常用的项目结构,整体结构树如下所示,读者可参考自行调整使用:

    |- web-project   // 项目根目录
        |- src
            |- main                  // 业务逻辑
                |- assembly          // 基于maven assembly插件的服务化打包方案
                    |- bin           // 模块脚本(启动、停止、重启)
                    |- sbin          // 管理员角色使用的脚本(环境检查、系统检测等等)
                    |- assembly.xml  // 配置文件
                |- java              // 源代码
                    |- com
                        |- sharkchili
                            |- www
                                |- system
                                    |- annotation     // 注解
                                    |- aspect         // 面向切面编程
                                    |- config         // 配置文件POJO
                                    |- filter         // 过滤器
                                    |- constant       // 存放常量
                                    |- utils          // 工具
                                    |- exception      // 异常
                                    |- controller     // 控制层(将请求通过URL匹配,分配到不同的接收器/方法进行处理,然后返回结果)
                                    |- service        // 服务层接口
                                        |- impl       // 服务层实现
                                    |- mapper/repository // 数据访问层,与数据库交互为service提供接口
                                    |- entity/domain     // 实体对象
                                        |- dto // 持久层需要的实体对象(用于服务层与持久层之间的数据传输对象)
                                        |- vo // 视图层需要的实体对象(用于服务层与视图层之间的数据传输对象)
                                    |- *Application.java  // 入口启动类
                |- resources         // 资源
                    |- static        // 静态资源(html、css、js、图片等)
                    |- templates     // 视图模板(jsp、thymeleaf等)
                    |- mapper        // 存放数据访问层对应的XML配置
                        |- *Mapper.xml
                        |- ...
                    |- application.yml        // 公共配置
                    |- application-dev.yml    // 开发环境配置
                    |- application-prod.yml   // 生产环境配置
                    |- banner.txt    
                    |- logback.xml            // 日志配置
            |- test                  // 测试源码
               |- java               
                    |- com
                        |- sharkchili
                            |- www
                                |- system
                                    |- 根据具体情况按源码目录结构存放编写的测试用例
        |- target     // 编译打包输出目录(自动生成,不需要创建)
        |- pom.xml    // 该模块的POM文件
    |- sql            // 项目需要的SQL脚本
    |- doc            // 精简版的开发、运维手册
    |- .gitignore     // 哪些文件不用传到版本管控工具中
    |- pom.xml        // 工程总POM文件
    |- README.md      // 注意事项
External Libraries    // 相关JAR包依赖

小结

以上便是笔者对于项目分层规范的总结,希望对你有帮助。

我是 sharkchiliCSDN Java 领域博客专家开源项目—JavaGuide contributor,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号: 写代码的SharkChili 。 因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

参考

优秀的代码都是如何分层的? :https://mp.weixin.qq.com/s/KL3U4mpi2-bnyFjfKNDPTw

同事问我代码结构中 Manager层是干什么的:https://juejin.cn/post/7032680627069517854

SpringBoot项目的目录结构及名规范 :https://blog.csdn.net/goodjava2007/article/details/122275079

本文使用 markdown.com.cn 排版