一、基本介绍
GoFrame
作为一款工程化完备的基础开发框架,有其独特的框架设计理念,这一章节我们来介绍一下她的代码分层设计。对于服务端业务代码的分层设计模式中,我们比较常见的是MVC
设计模式和三层架构设计模式(3-Tier Architecture
)。
二、MVC
设计模式
我们先来回顾一下经典的MVC
设计模式。
![MVC设计模式 MVC设计模式](/download/thumbnails/3672442/image2021-1-5_0-4-8.png?version=1&modificationDate=1609776248307&api=v2)
图1. MVC设计模式
简要介绍
M
代表模型(Model
),表示业务规则封装。在MVC
的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
V
代表视图(View
),用户看到并与之交互的界面。比如由HTML
元素组成的网页界面,或者软件的客户端界面。MVC
的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。
C
代表控制器(Controller
),接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
这种设计模式比较简单,比较合适于需要服务端渲染页面的业务场景,对于SEO
来说也比较友好。但随着MVVM
开发模式的兴起,以及前端技术的快速发展,特别是一些前端开发框架如Vue
、React
、Angular
之类的项目出现,服务端的MVC
设计模式使用场景变得越来越少。
痛点描述
针对于业务逻辑并不是特别复杂的业务场景项目,MVC
还能游刃有余,但随着业务逻辑变得庞大复杂,MVC
设计模式的项目维护成本上升的问题变得越来越明显。特别是随着互联网项目微服务架构的发展,MVC
设计模式在大部分的互联网项目开发中变得越来越鸡肋。究其原因,主要的几点:
- 视图展示与数据操作方式的进一步剥离,特别是移动端的发展,前端
MVVM
框架的发展,我们大多数场景下已不再需要服务端渲染View
。 MVC
的代码分层设计模式其实粒度较粗:Model
层级的代码既维护着数据,也封装着业务逻辑,随着业务逻辑变得越来越复杂,这一层功能逻辑会变得越来越臃肿不易维护。- 对于团队管理来讲,
Controller
和Model
的职责边界比较模糊,对于开发人员写好代码的要求会比较高。概念看似简单,但是却难以适应工业化的软件生产。
三、3-Tier Architecture
GoFrame
框架推荐的代码分层设计模式为三层架构设计(3-Tier Architecture
),但在具体的实现中,对这种结构设计模式进行了一定的改进。
![](/download/thumbnails/3672442/image2021-1-5_1-0-5.png?version=1&modificationDate=1609779605768&api=v2)
图2. 三层架构设计模式
传统的三层架构设计如上图,将项目代码划分了三层,每一层有其独自的职责边界。但在大多数的场景中,我们常看到的是以下的分层结构,将数据结构模型再进一步地抽离了出来统一维护。
图3. 常见三层架构设计模型
表示层 - UI
User Interface
位于三层构架的最上层,与用户直接接触,主要是B/S
中的 WEB
页面,也可以是API
接口。表示层的主要功能是实现系统数据的传入与输出,在此过程中不需要借助逻辑判断操作就可以将数据传送到BLL
系统中进行数据处理,处理后会将处理结果反馈到表示层中。换句话说,表示层就是实现用户界面/API
接口功能,将用户的需求传达和反馈,并用BLL
或者是Model
进行调试,保证用户体验。
业务逻辑层 - BLL
Business Logic Layer
的功能是对具体问题进行逻辑判断与执行操作,接收到表现层UI
的用户指令后,会连接数据访问层DAL
,业务逻辑层在三层构架中位于表示层与数据层中间位置,同时也是表示层与数据层的桥梁,实现三层之间的数据连接和指令传达,可以对接收数据进行逻辑处理,实现数据的增删改查等功能,并将处理结果反馈到表示层UI
中,实现软件功能。
数据访问层 - DAL
Data Access Layer
是数据库的主要操控系统,实现数据的增删改查等操作,并将操作结果反馈到业务逻辑层BLL
。在实际运行的过程中,数据访问层没有逻辑判断能力,为了实现代码编写的严谨性,提高代码阅读程度,一般软件开发人员会在该层中实现通用数据能力进行封装(例如通过ORM
组件)来保证数据访问层DAL
数据处理功能。
模型定义层 - Model
模型定义也常用Entity
实体对象来表示,主要用于数据库表的映射对象,在信息系统软件实际开发的过程中,要建立对象实例,将关系数据库表采用对象实体化的方式表现出来,辅助软件开发中对各个系统功能的控制与操作执行。建立实体类库,进而实现各个结构层的参数传输,提高代码的阅读性。从本质上看,实体类库主要服务于表示层、业务逻辑层以及数据访问层,在三层之间进行数据参数传输,强化数据表示的简约性。
需要注意区分的是,这里的Model
和MVC
设计模式中的Model
虽然都是一个名字但是差别巨大,职责完全不同。
三层架构设计与MVC
由于MVC
也是三层结构,因此有的同学也会将MVC
笼统地归纳于三层架构设计中,从字面意义上来讲似乎也没什么问题。不过两者还是存在一定的区别。
![](/download/attachments/3672442/image2021-1-5_19-8-47.png?version=1&modificationDate=1609844927551&api=v2)
图4. 三层架构设计与MVC
可以看到,在三层架构设计中,UI
表示层即相当于MVC
的View
和Controller
层,原本在MVC
中这两层的逻辑应当是比较"轻量"的,因此被合并为一层进行统一管理也可以理解。比较重要的一点是,原本MVC
中的Model
被拆分为了BLL
和DAL
,即将业务逻辑与数据访问进行分离,将原本臃肿的Model
进行了进一步的解耦,有利于项目的更好维护。
四、更进一步了解
代码分层思想是最基础的工程设计入门,我们需要将分层思想进行落地,具体请参考:工程目录设计🔥
21 Comments
kevin zou
关于分层结构,想请教一下 跨表的事务层的代码应该是 在service层还是dao层
在展示的例子的工程没有看到关于事务相关的代码,还请指教,感谢
郭强
你好,涉及到数据库事务的业务逻辑是耦合的。一个事务会有一个业务
service
的入口点,涉及到多个数据表的操作,这些操作方法都是为事务服务的。你可以把事务的逻辑用service
来封装,service
根据业务情况可直接调用另外的service
或者dao
,调用的时候把事务对象tx
传递进去即可。eden
请问微服务架构中,跨微服务调用应该在哪一层实现?Java 中有个防腐层,在gf 中如何防腐?
admin888
可以在service下面再起一层微服务调用层,比如httpclient 层,专门处理微服务之间的调用,service 调用 httpclient 层,httpclient层的实体模型可以放到model层
刘羽禅
Dao层不仅仅是mysql的操作,
redis/mongoDB/Es等数据的操作 都应该封装在Dao层.
自从前后端分离以后,MVC的View已经单独剥离出去,
而剩下的MC,也逐渐演变成了CSDM
controller=>service=>dao=>model
而gf的看起来是
api=>service=>dao=>model
当然还有另外一种结构是
controller=>logic=>dao=>model
刘羽禅
当然,不管如何分层,本质都是一样的,为了代码更合理的解耦. 鄙人理解为 设计模式中的单一职能原则的体现.
白羽
确实在我学习的过程中对于上述描述的情况有深刻的体会。
对于Redis来讲,个人拙见:对于业务开发,我经常会将Redis放在service中,使Redis的使用直接体现在业务的真实逻辑中。
望指教此种方式不妥之处
感谢大佬
浅弋璃鱼
redis如果作为缓存的话, 可以个对数据库的操作放在一起; redis+DB 对外提供数据操作接口, 调用这个接口的一方不关心数据来自于redis还是DB
白羽
确实没有想到可以这样子来做
dabuge
gf 的看起来是 controller → service → logic → dao → model,api 层只是单纯的定义了输入和输出的接口格式,没有业务逻辑在里面
Fed
当然还有另外一种结构是
api=>controller=>service=>logic=>dao=>model
kam
常见问题解答中,第二个问题 “如何清晰界定和管理
service
和dao
的分层职责” ,看完之后还是有点云里雾里的感觉。解答先是抛出了一个问题,然后再引出一个场景例子,然后就没有然后了的感觉。最后并没有交代要怎么做才能明确分层....所以这块还是比较疑惑。有什么好的实践是一个职责分层的好例子呢?
fengzhichao
我也有此疑问。 譬如我们将一个http服务器看做一个数据服务器。 也就是DAO里面封装的都是request, 也可能包含业务。
FLY的狐狸
接口层实现叫handler感觉有点怪,一般handler都便业务处理~ 还是action或者controller感觉更容易接受~ 仅个人意见
小陈
大佬说的太对了, 我经常分不清应该写在M层还是C层
guanchunsheng
好家伙,涨姿势呀
admin888
service层调用service层经常会出现循环调用的问题,比如你的服务需要调用另一个人写的服务接口,而另一个服务接口里可能还会调用你的接口,对于这种一般怎么处理?
大耳朵牛爷爷
你俩一开始就设计错了
A→B
B→C
一种处理是:
放在第三方,相当于A→C , B→C
第二种就是放在一起
郭强
service
是接口层,具体实现在logic
层,所以不会存在循环调用的问题。hly
好家伙,我工作8年的高级php 才了解三层架构 和mvc是不同的
runrui.wang
这个和 Asp.Net Core 的设计是一样的, View, Control,Model , BLL , Service , DAL 。 Model贯穿所有层。对于简单的CURD页面,基本上不用写代码,只改前端排版。 go有点不好的地方是,不能有null,前后端大小写不能统一,这些地方都要手动改。