这是我的上一篇笔记:Gin框架小项目再实践 | 青训营 - 掘金 (juejin.cn)在上篇笔记中,我完成了一个任务清单的小项目,能够实现对任务的增删改查。但是项目的目录结构跟企业中实际开发的结构差别还是有点大,因此我决定学习并实践一下如何改造项目的目录结构。
前言
典型的Go语言后端项目目录结构通常包括以下内容:
- cmd: 项目的入口点,包含主要的应用程序逻辑,可以有多个子命令。
- internal: 内部包,仅在项目内部使用,用于隐藏实现细节,防止外部包的直接访问。
- pkg: 可导出的库代码,其他项目可以使用的公共功能。
- api: 存放API定义、协议文件(如Protobuf或OpenAPI规范)和相关文档。
- web: Web服务器和处理程序,包括路由、中间件、静态资源等。
- db: 数据库模型、迁移脚本和数据库访问代码。
- config: 配置文件和配置加载代码。
- middleware: 自定义中间件代码,如身份验证、日志记录等。
- service: 业务逻辑层,实现具体的业务功能。
- repository: 数据库访问层的接口和实现。
- utils: 工具函数、帮助函数和通用的辅助代码。
- tests: 单元测试、集成测试和端到端测试代码。
- scripts: 构建脚本、部署脚本和其他项目相关的实用脚本。
- docs: 项目文档,包括API文档、使用手册等。
这种结构可以根据项目的规模和需求进行调整,但它提供了一种良好的组织方式,有助于保持代码的清晰性和可维护性。 接下来,以我之前写的小项目为例,进行项目目录结构的改进。
目前的目录结构展示
使用tree /f命令查看目录结构如下:
接下来,将介绍各个层如何改造
Controller
用来控制路由进来以后,执行哪些函数。
首先,简历controller文件夹下的controller.go文件,之后将各个处理器封装成函数放进去。
将请求首页的代码
改为:
在controller.controller里编写具体的函数:
同理,将post请求改为如下样式:
其余几个结构均需要改名,封装,与上面的步骤相似,成果如下:
dao层
即database access object 数据操作对象,我们将所有与数据库有关的代码移到这个目录下的mysql.go中:
main.go中初始化数据库的部分就变成了:
err := dao.InitMySQL()
dao.DB.AutoMigrate(&Todo{})
defer dao.Close() //程序退出关闭数据库连接
model
这里主要存放模型的定义,以及对这个模型的增删改查操作。
改造之后,应该把具体的增删改查的逻辑放在model层,而controller层应该只用调用model层的逻辑就可以了。如果逻辑比较复杂,controller层应该调用service层,service层再调用model层 即步骤应该是:
url -->controller -->service -->model
请求来了-->控制器-->业务层-->模型层的增删改查
以controller层中的GetTodoList函数为例,原本controller中代码如下:
func GetTodoList(c *gin.Context) {
var todoList []Todo
if err = DB.Find(&todoList).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, todoList)
}
}
修改完代码如下:
func GetTodoList(c *gin.Context) {
todoList, err := models.GetAllTodo()
if err != nil {
c.JSON(
http.StatusOK,
gin.H{"error": err.Error()},
)
} else {
c.JSON(http.StatusOK, todoList)
}
}
同时,models.GetAllTodo()函数的定义如下:
func GetAllTodo() (todoList []*Todo, err error) {
if err := dao.DB.Find(&todoList).Error; err != nil {
return err
}
return nil, err
}
同理,将controller层中的DeleteATodo函数改造成:
func DeleteATodo(c *gin.Context) {
id, ok := c.Params.Get("id")
if !ok {
c.JSON(http.StatusOK, gin.H{"error": "无效的id"})
return
}
if err := models.DeleteATodo(id); err != nil {
c.JSON(http.StatusOK, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, gin.H{id: "deleted"})
}
}
modeler层中的DeleteATodo函数如下:
func DeleteATodo(id string) (err error) {
err = dao.DB.Where("id=?", id).Delete(&Todo{}).Error
}
routers
main.go中还包含很多关于路由注册代码,其实这部分代码也是可以单独拿出来的。 此时main函数的代码如下:
非常的简洁
routers.go的代码如下:
改造完的目录结构展示
最后项目也推送至了github上面,感兴趣的同学可以下载下来研究一下:yehowlong/bubble: Gin框架小项目再实践之任务清单增删改查 (github.com)
踩的一些小坑
老是容易把函数的返回值(第二个括号里的参数)写到函数的参数列表里面去(第一个括号里的参数)