一,简介
我们做一个大项目会把项目分解成很多不不同的模块(Module),通常分为Dao,Service,Controller,Model,Utils。
有没有其实都可以,叫不叫这些名字也无妨,你要把Service的内容全写在Controller里也没问题,你要把Utils的工具函数分散在所有需要用的文件也Ok。但是久而久之,一堆人做一件事做的多了,就会形成这些约定俗成的部分,就好比形成了人行道,车行道,形成了红绿灯,当然这些终归还没有加进标准里去,所以你遵不遵守,都靠你自己。
所以,项目中是否包含这些模块或者单词,和你的项目结构是否完善一毛钱关系没有。但是当你的项目结构相对完善的时候,你会发现有这样一些角色的存在。
这一切都是为了实现高内聚度低耦合度,便于维护,也就是在软件生命周期中达到好的可用性以及总体上的低成本。但不应把这些当作教条,甚至不知道为了什么这样做。
二,项目分层
Model 模型
通常会有很多Model,就是数据库中的很多表定义。
Model 的作用就是数据的抽象,描述了一个数据的定义,Model的实例就是一组组的数据。整个系统都可以看成是数据的流动,既然要流动,就一定是有流动的载体。一条业务流就是对应一条或者多条数据流,
对于初学者而言,第一个要学会,就是建模,把业务逻辑映射成数据模型。
DAO 数据持久层
Dao一般而言,都是用来和底层数据库通信,负责对数据库的增删改查。
更直观一些,一个DAO对象 Article,包含 createArticle(), updateArticle(), deleteArticle()等方法。奇奇怪怪的SQL语句可以写在各个方法中。
这样可以解决,对象范例和关系范例这两大领域之间存在“阻抗不匹配”。这样分层也让controller 和 service 层更为干净。
本质上来说,DAO并不一定要和数据库有联系,DAO只是 Data Access Object(数据存取对象)的缩写,所以只要是把数据持久化包装成一个对象的访问(读写),这种对象都可以被称之为DAO,譬如,用JSON格式存到硬盘上。
Util 工具
Util就是工具,开发的过程中会产生许多奇奇怪怪的私有方法,有时候还都是重复的。这时候我们就可以把这些私有方法提取出来作为公用的方法。
像是URL编码或者解码,或是自创的加密签名算法等等。Util 代表他和业务逻辑是不相关的。通常命名也是ArticleUtil,CommentUtil之类的。
Util一般都有明确的输入和输出结果,都可以进行单元测试。
Service 服务
Service 比 Util 的概念大很多,它的重点是在于提供一个服务。这个服务可能包括一系列的数据处理,也有可能会调用多个Util,或者是调用别的服务。
技术上讲,一个 Service 可以调用其他 Service ,有人建议不要这样做,除非这个被调用的 Service 是公用的工具类。比如,有人给 Service 分了内外两层, 所有业务逻辑都放在 Service 里, 可复用的逻辑放在内层, 不复用的放在外层。这种情况会造成发布版本时,Service 变的不稳定。如果只允许 Controller 调用 Service,不允许 Service 互相调用,就可以避免这样的事。但是,这样遇到前后台统一业务逻辑的时候,代码要写两份。
综上所述,基本原则是 Service 不允许互相调用,保持 Controller 到 Service 到数据库的单线调用,然后在需要的时候打破这个规则,用 Service 调用 Service。
Service一般而言,都是包含有业务逻辑的,很少能做单元测试。
Controller 控制器
Controller 层是对客户端传来的数据做处理,传给 Service 层,然后把 Service 层产生的数据,返回给客户端。
比如,用户传了一个 pageSize,但是有可能传 null,这个时候你可以再进行一次转换成默认的10;然后调用Service 的方法,返回结果给到前端。
三,案例
案例一
阿里巴巴Java开发手册中的分层,甚至加入了DDD(领域模型)。
开放接口层:可直接封装 Service 接口暴露成 RPC 接口; 通过 Web 封装成 http 接口; 网关控制层等。
终端显示层:各个端的模板渲染并执行显示层。 当前主要是 velocity 渲染, JS 渲染, JSP 渲染,移动端展示层等。
Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
Service 层:相对具体的业务逻辑服务层。
Manager 层:通用业务处理层,它有如下特征:
1)对第三方平台封装的层,预处理返回结果及转化异常信息;
2)对 Service 层通用能力的下沉,如缓存方案、 中间件通用处理;
3)与 DAO 层交互,对 DAO 的业务通用能力的封装。
DAO 层:数据访问层,与底层 MySQL、 Oracle、 Hbase 进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。
案例二
SpaceX-API是 r/SpaceX 社区开源的用于火箭、核心舱、太空舱、发射台和发射数据的开源 REST API。
SpaceX-API 技术细节:
部署在美国中部的 Linode 服务器上
在 Koa 框架中使用 Node.js
使用 Redis、Nginx 和 Cloudflare 进行内容缓存
使用 Jest 和 Supertest 进行测试
使用 Circle CI 进行持续集成 / 部署
所有数据都存储在 MongoDB Atlas 3 节点副本集集群中
夜间 mongodump 数据库备份
SpaceX-API 无视 MVC
下面是 SpaceX-API 的三个版本,v3版本的时候,还有点mvc的影子,路由和控制器还是分开的。其中有个 v4-experimental 版本,直接使用 go 语言写的。v4版本的时候,routes和controllers不见了,弄出来一个services,下面按版本和模块分文件夹,model和routes放在一个文件夹中。最新的版本应该是v5版本,services改名routes,按模块和版本分文件夹,models 挪出去,单独放在一个文件夹。
感觉 SpaceX-API 完全是想怎么写就怎么写,完全无视 MVC,中间还换了一次 go 语言。
案例三
这个星球上40%以上的网站使用的是 WordPress,完全没有用这种分层的概念。
实践
在实践中,很多小项目的业务逻辑都在Controller中进行处理,服务层只负责一些增删改查的方法。甚至增删改查也在控制器。这样会造成完全无法单元测试和后期维护性差。特别简单的项目和没有“以后”的项目可以这样做。
还有下面这样的观点:
“如果一个项目的生命周期比较长,要不要分层,项目一开始就要确定,这是架构师的职责。”
“当项目达到一定规模,再重构,不要想着一个万全的设计。”
“因为是面向数据库编程,但凡没有或者不怎么依赖数据库的程序,三层模型全面崩塌。”
看了 SpaceX-API,感觉自己玩,怎么来都可以;大家一起玩的话,商量着来。
参考:
https://www.zhihu.com/question/58410621
https://segmentfault.com/q/1010000018560036/a-1020000018615733
https://blog.csdn.net/qq_22771739/article/details/82344336
https://www.zhihu.com/question/58410621/answer/623496434
《阿里巴巴java开发手册》
https://github.com/r-spacex/SpaceX-API
https://www.zhihu.com/question/484268189/answer/2112084953
修改时间 2021-12-16