Go语言中常见100问题-#12 项目结构最佳实践

创建一个好的Go项目结构并不是一件容易的事情,由于Go语言在设计包和模块方面提供了很大的自由度,因此在这方面没有通用的最佳实践。本文将首先讨论创建项目的常用组织结构,然后讨论一些最佳实践,给出改进项目组织方式的方法。

项目结构模板

Go语言维护者对构建Go项目结构没有严格的约定,在github上有一个称为标准Go项目结构的模板(github.com/golang-stan...%25EF%25BC%2589%25EF%25BC%258C%25E6%25B3%25A8%25E6%2584%258F%25E8%25AF%25A5%25E6%25A8%25A1%25E6%259D%25BF%25E4%25B8%258D%25E6%2598%25AFGo%25E5%25AE%2598%25E6%2596%25B9%25E6%258F%2590%25E4%25BE%259B%25E7%259A%2584%25E3%2580%2582%25E5%25A6%2582%25E6%259E%259C%25E6%2588%2591%25E4%25BB%25AC%25E7%259A%2584%25E9%25A1%25B9%25E7%259B%25AE%25E5%25BE%2588%25E5%25B0%258F%25EF%25BC%2588%25E5%258F%25AA%25E6%259C%2589%25E5%2587%25A0%25E4%25B8%25AA%25E6%2596%2587%25E4%25BB%25B6%25EF%25BC%2589%25EF%25BC%258C%25E6%2588%2596%25E8%2580%2585%25E5%2585%25AC%25E5%258F%25B8%25E5%2592%258C%25E9%25A1%25B9%25E7%259B%25AE%25E7%25BB%2584%25E5%25B7%25B2%25E7%25BB%258F%25E6%258C%2587%25E5%25AE%259A%25E4%25BA%2586%25E9%25A1%25B9%25E7%259B%25AE%25E7%25BB%2593%25E6%259E%2584%25E8%25A7%2584%25E8%258C%2583%25EF%25BC%258C%25E9%2587%258D%25E6%2596%25B0%25E8%25B0%2583%25E6%2595%25B4%25E6%2588%2596%25E8%25BF%2581%25E7%25A7%25BB%25E5%2588%25B0%25E4%25B8%258A%25E8%25BF%25B0%25E6%25A8%25A1%25E6%259D%25BF%25E6%25A0%25BC%25E5%25BC%258F%25E5%258F%25AF%25E8%2583%25BD%25E4%25B8%258D%25E5%2580%25BC%25E5%25BE%2597%25E3%2580%2582%25E5%25A6%2582%25E6%259E%259C%25E9%25A1%25B9%25E7%259B%25AE%25E8%25BF%2598%25E6%25B2%25A1%25E6%259C%2589%25E7%25BB%2593%25E6%259E%2584%25E8%25A7%2584%25E8%258C%2583%25EF%25BC%258C%25E9%2582%25A3%25E5%2589%258D%25E9%259D%25A2%25E8%25BF%2599%25E4%25B8%25AA%25E7%25BB%2593%25E6%259E%2584%25E5%2580%25BC%25E5%25BE%2597%25E5%258F%2582%25E8%2580%2583%25E5%2580%259F%25E9%2589%25B4%25E3%2580%2582%25E7%258E%25B0%25E5%259C%25A8%25E6%2588%2591%25E4%25BB%25AC%25E6%259D%25A5%25E7%259C%258B%25E7%259C%258B%25E8%25BF%2599%25E4%25B8%25AA%25E7%25BB%2593%25E6%259E%2584%25E6%25A8%25A1%25E6%259D%25BF%25E7%259A%2584%25E5%25B8%2583%25E5%25B1%2580%25EF%25BC%258C%25E9%2583%25BD%25E6%259C%2589%25E4%25BA%259B%25E4%25BB%2580%25E4%25B9%2588%25E5%2586%2585%25E5%25AE%25B9%25EF%25BC%259A "https://github.com/golang-standards/project-layout)%EF%BC%89%EF%BC%8C%E6%B3%A8%E6%84%8F%E8%AF%A5%E6%A8%A1%E6%9D%BF%E4%B8%8D%E6%98%AFGo%E5%AE%98%E6%96%B9%E6%8F%90%E4%BE%9B%E7%9A%84%E3%80%82%E5%A6%82%E6%9E%9C%E6%88%91%E4%BB%AC%E7%9A%84%E9%A1%B9%E7%9B%AE%E5%BE%88%E5%B0%8F%EF%BC%88%E5%8F%AA%E6%9C%89%E5%87%A0%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%89%EF%BC%8C%E6%88%96%E8%80%85%E5%85%AC%E5%8F%B8%E5%92%8C%E9%A1%B9%E7%9B%AE%E7%BB%84%E5%B7%B2%E7%BB%8F%E6%8C%87%E5%AE%9A%E4%BA%86%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%A7%84%E8%8C%83%EF%BC%8C%E9%87%8D%E6%96%B0%E8%B0%83%E6%95%B4%E6%88%96%E8%BF%81%E7%A7%BB%E5%88%B0%E4%B8%8A%E8%BF%B0%E6%A8%A1%E6%9D%BF%E6%A0%BC%E5%BC%8F%E5%8F%AF%E8%83%BD%E4%B8%8D%E5%80%BC%E5%BE%97%E3%80%82%E5%A6%82%E6%9E%9C%E9%A1%B9%E7%9B%AE%E8%BF%98%E6%B2%A1%E6%9C%89%E7%BB%93%E6%9E%84%E8%A7%84%E8%8C%83%EF%BC%8C%E9%82%A3%E5%89%8D%E9%9D%A2%E8%BF%99%E4%B8%AA%E7%BB%93%E6%9E%84%E5%80%BC%E5%BE%97%E5%8F%82%E8%80%83%E5%80%9F%E9%89%B4%E3%80%82%E7%8E%B0%E5%9C%A8%E6%88%91%E4%BB%AC%E6%9D%A5%E7%9C%8B%E7%9C%8B%E8%BF%99%E4%B8%AA%E7%BB%93%E6%9E%84%E6%A8%A1%E6%9D%BF%E7%9A%84%E5%B8%83%E5%B1%80%EF%BC%8C%E9%83%BD%E6%9C%89%E4%BA%9B%E4%BB%80%E4%B9%88%E5%86%85%E5%AE%B9%EF%BC%9A")

  • /cmd 项目主要的应用程序. foo应用程序的main.go应该位于/cmd/foo/main.go中。

  • /internal 私有的应用程序代码库,这里面的代码是不希望被其它人导入的。

  • /pkg 外面的应用程序可以使用的代码库,是向其它人公开的公共代码。

  • /test 存储测试数据和代码。Go语言中的单元测试文件与源文件通常都在一个包中。但像公共API测试或集成测试代码应该存放在/test中。

  • /configs 存放配置文件

  • /docs 存放设计和用户文档

  • /examples 应用程序或公共库函数的实例程序

  • /api api接口定义文件(Swagger, Protocol Buffers等)

  • /web web应用程序的资源文件(静态图片等)

  • /build 打包和持续集成(CI)文件

  • /scripts 用于分析、安装等脚本文件

  • /vendor 应用程序的依赖文件(例如Go模块的依赖库)

可以看到上面的标准结构中没有/src目录,这是因为/src目录太泛了,因此采用了/cmd、/internal和/pkg这种目录。

NOTE:在2021年,Go语言的核心维护者 Russ Cox对上面的项目结构表达反对意见。尽管它号称是Go项目标准结构,但不是官方的标准,有误导人嫌疑。对于项目结构,没有强制性约定必须采用上述模板。我们必须意识到这一个点,唯一注意的是项目中的各个模块结构要保持一致,达成统一。避免在不同的结构之间发生迁移,这会浪费时间。

包组织结构

在Go语言中,没有子包的概念。但是,我们可以在子目录中创建包。下面是标准库net中的目录结构。net既充当包,又充当包含其他包的目录。但是net/http包不继承net或对net包具有特定的访问权限。外界能看到net/http中可导出的元素。子目录的主要好处是将包中代码保存在具有高内聚性的地方。

console 复制代码
/net

/http

client.go

...

/smtp

auth.go

...

addrselect.go

...

对于Go包的组织形式,有不同的观点。例如,我们应该按业务类型还是按层来组织应用程序,这取决于自己的喜好。我们可能倾向于按业务类型(例如客户业务,合同业务等)对代码进行分组,或者我们倾向于遵循六边形原则对其进行分组。只要选择出了适合我们的方法,保持统一即可。

对于软件包,我们应该遵循一些最佳实践。首先,应该避免过渡设计,因为这可能会使得项目过于复杂。当我们搞清楚了项目包含的内容后,最好使用一个简单的形式组织并让项目不断的发展,而不是强迫自己预先制定完美的结构。

包的粒度是另一个需要考虑的重要因素,我们应该避免有几十个包含一两个文件的小包。如果这样设计,可能错过了这些包之间的一些逻辑联系,使得项目更难让人理解。此外,我们也应该避免使用包含很多文件的大包。总之,对于包的粒度,我们不应该走极端,导致包极小或极大。

包的命名也应该谨慎考虑。众所周知,命名是程序开发中一件困难的事情。为了帮助用户理解Go项目,我们应该根据它提供的内容命名包,而不是它包含的内容。此外,包名要有意义。因此,包的名称应该简短、简洁和富有表现力,按照惯例,应该是一个小写单词。

对于包导出什么,规则非常简单。我们应该尽可能减少应该导出的内容,以减少包之间的耦合并隐藏不必要导出的元素。如果不确定是否要导出一个元素,应该默认它不导出,在后面发现需要导出时,再调整代码支持将其导出。我们还要注意一些特殊情况,例如,当我们对一个结构体对象调用 encoding/json 标准库对其进行序列化或反序列化时,该结构体对象的字段需要是可导出的(即首字母要大写),否则会忽略该字段。

组织好一个项目结构并不是一件简单的事情,遵循上述这些规则有助于我们更容易维护。记住一点,保持结构一致对于简化可维护非常有帮助。因此,应确保代码库中的代码尽可能保持一致。

相关推荐
ifanatic19 分钟前
[面试]-golang基础面试题总结
面试·职场和发展·golang
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
长风清留扬3 小时前
一篇文章了解何为 “大数据治理“ 理论与实践
大数据·数据库·面试·数据治理
周三有雨14 小时前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
爱米的前端小笔记14 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
好学近乎知o14 小时前
解决sql字符串
面试
不爱说话郭德纲16 小时前
聚焦 Go 语言框架,探索创新实践过程
go·编程语言
我明天再来学Web渗透19 小时前
【SQL50】day 2
开发语言·数据结构·leetcode·面试
程序员奇奥20 小时前
京东面试题目分享
面试·职场和发展
理想不理想v21 小时前
【经典】webpack和vite的区别?
java·前端·javascript·vue.js·面试