前言
我在升级之前做了比较充分的准备工作,深入研究了GoFrame V2新特性才决定升级的,并且总结了一篇文章:# 站在开发者的角度理解框架的设计思想。
区别于官方文档,我是站在开发者的角度,总结分享了V2版本相比于V1版本的优势,实践了我的有着130多个接口的# 开源电商项目的升级踩坑之旅,希望对大家有帮助。
目前开源电商系统V2版正在开发中,欢迎Star:https://github.com/wangzhongyang007/goframe-shop-v2
基于Gin+Gorm+VUE的集五福营销裂变项目也在脱敏中,后面会开源出来供大家学习使用。
先说结论
我决定用我的开源项目# 开源电商前后台API系统 实践升级之旅。
这是一个单体项目,适合新手入门,开发了超过了130个接口,不管是新手入门还是实践升级之旅都比较有代表性:包括了传统电商需要的基本功能,也有进阶高并发的解决方案。
欢迎Star:https://github.com/wangzhongyang007/goframe-shop
我最终结合自己的项目情况,决定用V2重写开源项目,而不是用V1升级到V2,因为我的工程设计思想和V2建议的工程结构差别很大。
经过仔细考量后,我认为V2的工程架构更好,虽然门槛稍高一点,但是在项目后期更易于维护。
实践出真知
我调研实操的经历,还是非常有参考价值的,我的项目不适合从V1升级到V2,而是用V2重写,并不代表你的项目不适合。这个经历真的就像“小马过河”一样。
升级之旅
下面就介绍一下我的升级之旅:
1. 首先查看之前的版本:
2. 替换依赖库
在工程文件中进行替换,把所有的:github.com/gogf/gf/ 全部替换为:github.com/gogf/gf/v2/
3.按提示解决错误
替换之后尝试运行,肯定会出错,但是没关系,提示什么错误就解决什么错误嘛:
我尝试根据提示执行 go get 相关依赖,但是并没有生效。
4.升级框架和CLI工具
突然想到我只是替换了项目中的依赖包,但是并没有更新GoFrame框架和GoFrame CLI工具。
而且通过查阅官方文档得知:GoFrame是支持v1和v2同时使用的,但是官方并不建议,因为同时使用会导致维护成本很高。
注意:框架是框架,cli是cli,两者不要混淆在一起。官方文档有详细介绍,不再重复阐述:官方文档[1]
在升级CLI工具时踩了很多坑,因为通过查阅官方文档,发现了很多种升级方式,但我在实践中前几种均未生效,最终是通过下面的方式升级成功的。
注意:如果是MacOS下使用zsh的小伙伴可能会遇到别名冲突问题,可以通过alias gf=gf来解决,运行一次之后gf工具会自动修改profile中的别名设置,用户重新登录(或者重开终端)就好了。
5. CLI升级成功
CLI升级安装成功的示例图:
查看gf cli版本 已经更新到了v2.2.0 最新版:
6.及时备份
因为升级CLI我花了好长时间,做了各种尝试,所以我决定及时提交git做好备份,养成好习惯:
在解决了CLI升级问题之后,我真的长舒了一口气。准备迎接新的挑战吧:
7.gtoken升级
解决gtoken的问题还是很简单的,升级gtoken就可以了,正如官方文档所说gtoken已经全面拥抱GoFrame V2版本了:
安装升级gtoken的教程如下:
- gopath模式:go get github.com/goflyfox/gtoken
- 使用go.mod添加:require github.com/goflyfox/gtoken latest
我是使用gopath模式安装的,很顺利;我们再继续解决新的报错:
8.gmvc问题
通过查阅文档得知,gmvc已经废除,以后也不再支持了:
9.最新版的gf CLI生成dao
准备迎接新的挑战吧,用最新版的gf CLI生成dao:
并没有迎接到暴风雨, 而是卡住了。
原因是这样的,正如我开篇说的:
V2版本的工程目录做了调整,官方说:如果你的V1项目使用的是GoFrame官方推荐的工程目录结构,可以参考最新的工程目录结构手动调整即可:工程目录设计[2]。
需要注意的是,最新的cli工具不再支持旧版工程目录的项目创建。
10.调整项目的目录结构
我们需要调整项目的目录结构,参考链接如下:https://GoFrame.org/pages/viewpage.action?pageId=30740166
我在项目根目录下,新建internal目录,然后执行 gf gen dao 还是很丝滑的。
11.分析V2的gf gen dao
我发现v2版本的cli不仅给我们生成了dao层,在model层中还细分了do层和entity层。
为什么这么设计,大家有时间可以看官方文档:https://GoFrame.org/pages/viewpage.action?pageId=30740166
在这里我只说结论:
- dao层用于数据访问,这是一层抽象对象,用于和底层数据库交互,仅包含最基础的 CURD 方法
- model层是结构模型,是数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义。
model中的do是领域对象,用于dao数据操作中业务模型与实例模型转换,由工具维护,用户不能修改。
model中的entity是数据模型,数据模型是模型与数据集合的一对一关系,由工具维护,用户不能修改。
12.再次备份
为了方便找回资料,我在修改目录结构之前也在本地做了备份,方便我一会复制粘贴代码,小伙伴们也可以借鉴一下。
13.调整目录结构
官方建议的工程目录
目录/文件名称 | 说明 | 描述 |
api | 对外接口 | 对外提供服务的输入/输出数据结构定义。考虑到版本管理需要,往往以api/v1...存在。 |
hack | 工具脚本 | 存放项目开发工具、脚本等内容。例如,CLI工具的配置,各种shell/bat脚本等文件。 |
internal | 内部逻辑 | 业务逻辑存放目录。通过Golang internal特性对外部隐藏可见性。 |
- cmd | 入口指令 | 命令行管理目录。可以管理维护多个命令行。 |
- consts | 常量定义 | 项目所有常量定义。 |
- controller | 接口处理 | 接收/解析用户输入参数的入口/接口层。 |
- dao | 数据访问 | 数据访问对象,这是一层抽象对象,用于和底层数据库交互,仅包含最基础的 CURD 方法 |
- logic | 业务封装 | 业务逻辑封装管理,特定的业务逻辑实现和封装。往往是项目中最复杂的部分。 |
- model | 结构模型 | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义。 |
- do | 领域对象 | 用于dao数据操作中业务模型与实例模型转换,由工具维护,用户不能修改。 |
- entity | 数据模型 | 数据模型是模型与数据集合的一对一关系,由工具维护,用户不能修改。 |
- service | 业务接口 | 用于业务模块解耦的接口定义层。具体的接口实现在logic中进行注入。 |
manifest | 交付清单 | 包含程序编译、部署、运行、配置的文件。常见内容如下: |
- config | 配置管理 | 配置文件存放目录。 |
- docker | 镜像文件 | Docker镜像相关依赖文件,脚本文件等等。 |
- deploy | 部署文件 | 部署相关的文件。默认提供了Kubernetes集群化部署的Yaml模板,通过kustomize管理。 |
resource | 静态资源 | 静态资源文件。这些文件往往可以通过 资源打包/镜像编译 的形式注入到发布文件中。 |
go.mod | 依赖管理 | 使用Go Module包管理的依赖描述文件。 |
main.go | 入口文件 | 程序入口文件。 |
因为我之前的目录结构也不是严格按照goFrame v1.xx示例设计的,而是根据项目的前后端需求自定义设计的。
所以意识到了:要想顺利升级,工作量还是非常大的,应该需要修改很多代码。
我参考文档修改了自己项目的目录:
13.1 迁移api层
我把之前写的对外接口相关的代码放到api层:
13.2 替换dao和model被引用的路径
迁移之后遇到了新问题:
咱们来分析一下,目前做的操作只是:
- 通过最新的gf cli工具在项目根目录的internal目录下生成了新的dao和model层;
- 删除了之前app目录下的dao层和model层。
那咱们全局替换一下imports的目录不就行了:
全局替换dao和model的目录:
- 把shop/app/dao替换为:shop/internal/dao
- 把shop/app/model替换为:shop/internal/model
OK,顺利的解决了上面的问题。
14.解决神奇的问题
又遇到了新的问题:提示我 xxx is not in GOROOT。
我尝试通过用goland打开我的go/src目录,而不是直接打开shop工程目录,来解决问题:
这招比较好用,项目的依赖不再全部飘红,并且goland也给出了提示:
15.解决构建约束问题
分析一下原因:新的工程目录使用了internal目录,进行了约束。意思也就是除了对外暴露接口的方法放在api目录,其他不需要对外的逻辑都要放在internal中:
好吧,啥也别说了,继续修改:
我将之前处理业务逻辑的app目录中的文件迁移到internal目录中。
温馨提示:你的项目是哪个目录不重要,总之,按照GoFrame V2的原则,只有api目录用于暴露给外部,不需要暴露给外部的逻辑全部放在internal目录中:
16.项目工程目录和V2建议的工程目录统一
我结合自己的项目情况,移动和重命名了业务逻辑文件和目录,整体还是很顺滑的:
我之前的项目以library作为公共程序包,官方建议使用utility目录,还是以官方为准吧,这样以后在社区中沟通也能降低理解难度:
经过目录调整,修改后的目录结构和官方建议的目录结构基本一致了:
然后继续各种运行,报错,解决错误,整体上都比较好解决,就不做记录了。
值得分享的经验就是:不要怕报错多,整体看一遍,找找共同特点。绝大多数都是可以根据提示顺利解决的问题。
17.迁移业务逻辑
在迁移业务逻辑时发现了新问题:
我通过研究V2的官方示例得知,service层内部每个文件都以接口的方式定义,且service层是能够通过代码自动生产的:
如何预定义接口需要实现的方法呢?
答案是:在logic层编写代码,通过工具生成对应的service文件;在logic层的init()方法中调用service层的RegisterXXX()方法进行服务注册,由此可见复杂的逻辑都是在logic层处理的。
18. 通过CLI生成service
通过cli生成service是非常重要的操作,因为这部分代码必须工具生成,所以我们要先了解这个知识点,先说我的结论:
- 业务模块之间的依赖通过接口化解耦,将原有的service分类调整为接口目录。这样每个业务模块将会各自维护、更加灵活。
- 该命令通过分析给定的logic业务逻辑模块目录下的代码,自动生成service目录接口代码。
- 生成接口及服务注册文件
更多介绍可以查看官方文档:接口维护-gen service[3]
19. 反思
写到这里,目前的心得体会是一定要搞清楚v2版本的设计思想,再从v1升级到v2,否则升级到一半会导致无从下手。
因为通过V2版本的CLI工具生成的dao、model 和v1版本版本是不一致的。
同时V2版本也支持gf gen service的方式,统一我们的接口维护方式。
再次强烈大家看我这篇文章:# 站在开发者的角度理解框架的设计思想
从框架开发者和使用者两个角度去学习升级V2的知识点。
20.“分水岭”问题
到这里是一个重要的分水岭了:是升级迁移,还是直接用V2重写,这个没有标准答案,咱们应该在吃透了V2新特性和工程设计思想后,结合已有的项目特点去决定。
我结合自己的项目特点,经过仔细分析,再加上和GoFrame社区大佬们的沟通,我决定用GoFrame V2版本直接重写,而不是在已有项目上做迁移。
因为我的项目结构和v2的设计思想差别太大了,升级无异于重写,甚至可能花费的精力更多。
我开源项目的工程设计是这样的:
- 首先在app目录新建了system目录,用于管理多个终端:
backend对应电商平台的后台接口
frontend对应电商平台的前台接口。
- 在每个终端下又都是按照功能模块做了区分,每个功能模块都会创建一个单独的目录。我们以admin目录举例:
- admin.go:定义请求和响应的结构体
- admin_api.go:定义了对外api的方法,调用下方admin_service.go中封装的逻辑
- admin_service.go:编写业务逻辑,通过dao操作DB
显然,我的架构设计和GoFrame V2的工程设计思路差别是很大的,经过慎重思考,我决定基于V2最新版本重写开源项目,也欢迎小伙们加入我们。
总结
在遇到“分水岭”问题之前,GoFrame从v1升级到v2的过程还是比较顺滑的。
我也在社区中和大家沟通了升级问题:有的大佬表示自己是新开一个项目用V2重写的;有的大佬表示升级很简单,就是目录变更一下,业务逻辑还是可以用的,费不了多长时间。
这个问题就像“小马过河”的故事一样,大家还是要结合自己的项目情况:
- 如果你是按照V1版本建议的工程目录或者你的设计思想本身就和V2版本比较契合,那就直接升级。
- 如果你像我一样,已有项目的工程目录和设计思路和V2版本有较大区别,并且在认真学习v2版本的工程实践和新特性之后,觉得V2版本更适合你项目的后期维护迭代,那就抓紧用V2直接重写项目吧。
相关资料
[1] 官方文档: https://GoFrame.org/pages/viewpage.action?pageId=1115790
[2] 工程目录设计: https://GoFrame.org/pages/viewpage.action?pageId=30740166
[3] 接口维护-gen service: https://GoFrame.org/pages/viewpage.action?pageId=49770772
[4] Go开源电商项目教程-教程大纲+开源地址: https://b23.tv/hdOpOTp
本文转载自微信公众号「 程序员升级打怪之旅」,作者「王中阳Go」,可以通过以下二维码关注。
转载本文请联系「 程序员升级打怪之旅」公众号。