Go模块布局管理文档翻译理解
作者:cainmusic
Organizing a Go module
原文:https://go.dev/doc/modules/layout
新的Go开发者经常会提出一个问题:如何组织一个Go项目?这个问题主要指文件和文件夹布局方面。
本文的目标是提供一些有助于回答这个问题的准则。为了充分利用本文,请确保你熟悉Go模块的基本概念,你可以阅读下面两个文档:
- the tutorial : https://go.dev/doc/tutorial/create-module
- managing module source : https://go.dev/doc/modules/managing-source
Go项目可以包括包、命令行程序或者是这两者的组合,本文按项目类型来组织。
Basic package
一个基本的Go包可以把所有代码放在项目的根目录中。该项目由单个包,即单个模块组成。
对于最简单的包,只需要一个Go文件(不包含test文件),项目结构是:
project-root-directory/ go.mod modname.go modname_test.go
包名与模块名的最后一个路径组件匹配,modname中的包声明代码如下:
(译者注:实际上不同名也可以,但易用性很差,不建议这样做,或者至少要在文档中明确说明)
package modname // ... package code here
如果这个目录上传到GitHub存储库github.com/someuser/modname
,那么go.mod文件中的模块行应表述如下:
module github.com/someuser/modname
用户若需要依赖这个包,可以按如下方法导入:
import "github.com/someuser/modname"
一个Go包可以被分隔为多个文件,所有文件处于同一目录中,如:
project-root-directory/ go.mod modname.go modname_test.go auth.go auth_test.go hash.go hash_test.go
目录中的所有文件都需要声明包名modname。
Basic command
基本的可执行程序(或命令行工具)是根据其复杂程度和代码量大小来构建的。
最简单的程序由一个定义了main函数的Go文件组成。
更大的程序可以将代码分隔到多个文件中,并且所有文件都声明包名main,如:
project-root-directory/ go.mod auth.go auth_test.go client.go main.go
这里的main.go文件包含main函数,但这只是一种惯例。“主”文件也可以被叫做modname.go(以匹配包名)或其他任何名字。
如果这个目录上传到GitHub存储库github.com/someuser/modname
,那么go.mod文件中的模块行影表述如下:
module github.com/someuser/modname
用户若需要使用这个程序,可以按如下方法安装:
$ go install github.com/someuser/modname@latest
Package or command with supporting packages
更大的包或命令行工具(command)受益于将一部分功能分隔到支援包中。
(译者注:这段翻译可读性有点差,我个人觉得主要是原文表意就不太明确,主要想表达的应该是更大的包或command基于可读性和功能隔离等需求,应该将部分功能分隔到supporting packages中)
起初,我们建议将这些包放入internal目录中,这样可以防止外部依赖或避免不需要的代码暴露。由于外部程序不能导入我们internal目录中的代码,我们可以任意重构或者改变代码,而不会影响外部用户。
这样的包的项目结构如下:
project-root-directory/ internal/ auth/ auth.go auth_test.go hash/ hash.go hash_test.go go.mod modname.go modname_test.go
modname.go文件声明包名modname,auth.go文件声明包名auth,依此类推。
modname.go可以按如下方法导入auth包:
import "github.com/someuser/modname/internal/auth"
使用internal目录保存支援包的命令行工具的布局类似,区别仅为,根目录中的文件(都)应该声明包名main。
Multiple packages
一个模块可以由多个可导入的包组成,每个包都有自己的目录,并可以分层构建,如下:
project-root-directory/ go.mod modname.go modname_test.go auth/ auth.go auth_test.go token/ token.go token_test.go hash/ hash.go internal/ trace/ trace.go
提醒一下,我们假设go.mod中的模块行如下:
import "github.com/someuser/modname"
子包可以被其他用户用如下方法导入:
import "github.com/someuser/modname/auth" import "github.com/someuser/modname/auth/token" import "github.com/someuser/modname/hash"
而放在internal/trace目录中的trace包则不能被外部模块导入。
建议尽可能的将包保存在internal目录下。
(译者注:这个说法原文并没有给出明确的解释,理由和是否遵循这一建议大家可以各自发挥)
Multiple commands
一个项目中的多个可执行程序通常有不同的目录,如下:
project-root-directory/ go.mod internal/ ... shared internal packages prog1/ main.go prog2/ main.go
在各自的目录中,程序的Go文件声明包名main。顶级的internal目录可以存放多个程序共用的包。
用户可以按如下方法安装这些程序:
$ go install github.com/someuser/modname/prog1@latest $ go install github.com/someuser/modname/prog2@latest
一个常见的约定是将同一个仓库的命令行程序都放入cmd目录。
在一个只由命令行程序构成的项目中,这不是严格必须的。
但在由可导出包和命令行程序构成的混合仓库中,这种用法是非常有效的,接下来便会讨论。
Packages and commands in the same repository
有时候一个仓库既提供可导出包,也提供具有相关功能的可安装命令行程序。如下:
project-root-directory/ go.mod modname.go modname_test.go auth/ auth.go auth_test.go internal/ ... internal packages cmd/ prog1/ main.go prog2/ main.go
假设这个模块被命名为github.com/someuser/modname
。
用户可以按照下面的方法导入模块,或者安装命令:
Go文件中:
import "github.com/someuser/modname" import "github.com/someuser/modname/auth"
命令行中:
$ go install github.com/someuser/modname/cmd/prog1@latest $ go install github.com/someuser/modname/cmd/prog2@latest
Server project
Go是实现服务器的常用语言。
考虑到服务器的种类繁多,比如协议(REST、gRPC等)、部署、前端、容器化、脚本等等,这类项目的目录结构存在很大差异。
我们重点介绍其中Go语言编写的项目部分。
由于服务器通常是一个(或多个)自包含的二进制文件,因此服务器程序通常不需要对外导出包,所以建议将提供服务器逻辑的Go模块放入internal目录。
另外,由于程序可以包含其他非Go文件,所以将所有Go命令行程序放在cmd目录下是个好主意。
如下:
project-root-directory/ go.mod internal/ auth/ ... metrics/ ... model/ ... cmd/ api-server/ main.go metrics-analyzer/ main.go ... ... the project's other directories with non-Go code
考虑到服务器仓库可能产生一些对外部程序也十分有用的包,最好的方法是将这些包剥离出来形成单独的模块。
(译者注:这里并没有详细说明建议剥离出的包是当前项目导出包还是拆分成单独的项目,大家各自发挥)
译者结语
前面的内容基本就是原文的翻译了。
简单总结如下:
Go的包隔离层级为:目录。同一个目录下的Go文件都声明为同一个包。
Go语言的包管理遵循下面的几条原则:
- 简便。最简便的包和程序不需要复杂的布局。
- 私用。尽量使用internal目录隔离不需要对外导出的功能。internal目录约束在1.4版本中引入。
- 隔离。隔离保证复杂功能布局下的可读性,如cmd目录。
- 拆包。如果一个模块的功能本身在一定程度上是完备的,可以考虑拆分。
以上就是Go模块布局管理文档翻译理解的详细内容,更多关于Go模块布局管理的资料请关注脚本之家其它相关文章!