老卫同学
发布于 2024-02-22 / 44 阅读
0
0

Go开发-Go项目结构的最小标准布局

转自:Go项目目录该怎么组织?官方终于出指南了! https://mp.weixin.qq.com/s/zbV2UwHYvgsuwnXR2Vr04Q

长久以来,在Go语言进阶的学习和实践之路上,Go项目目录究竟如何布局一直是困扰大家的一个问题,这是因为Go官方针对这个问题迟迟没有给出说法,更没有提供标准供大家参考。仅有Go语言项目技术负责人Russ Cox在一个开源项目的issue中给出了他[关于Go项目结构的最小标准布局的想法[1]](#参考资料)。

近期Go官方文档集合中新增了一篇名为[“Organizing a Go module”的文档[6]](#参考资料),细读之后,我发现这不就是大家期待已久的Go项目目录布局的官方指南吗!

在这篇文章中,我们就来看看这份[官方指南[7]](#参考资料),看看官方推荐的Go项目目录布局是什么样子的。

1.Go项目的类型

我们知道Go项目(project)一般有两类:library和executable。library是以构建库为目的的Go项目,而executable则是以构建二进制可执行文件为目的的Go项目。

“Organizing a Go module”这篇文档也是按照Go项目类型为Gopher提供项目布局建议的。这篇文档将library类的项目叫作package类,executable类的项目叫作command。下面的示意图展示了“Organizing

a Go module”这篇文档的说明顺序:
00001.png

从图中看到,“Organizing a Go module”这篇文档总共给出7种项目的布局建议。接下来,我们就来逐一看一下。

2. 官方版Go项目目录布局指南

2.1 basic package

我们先从package类开始。最简单的package类的Go项目是basic package,下面就是一个basic package类的项目目录布局的示例:

project-root-directory/

├── go.mod

├── modname.go

└── modname_test.go

或

project-root-directory/

├── go.mod

├── modname.go

├── modname_test.go

├── auth.go

├── auth_test.go

├── hash.go

└── hash_test.go

我们看到basic

package类项目非常简单,repo下面只有一个导出package,这个package包含一个或多个包源文件。以repo托管在github上为例,如果这个repo的url为github.com/someuser/modname,那么该repo下的module

root和导出package的导入路径通常与repo url一致,都为github.com/someuser/modname。

你的代码要依赖该module,直接通过下面import语句便可以将该module导入:

import "github.com/someuser/modname"

2.2 basic command

和basic package一样,basic command类项目是以构建可执行二进制程序为目的的Go项目中最简单的一类。下面是basic command类项目的一个示例:

project-root-directory/

├── go.mod

└── main.go

或

project-root-directory/

├── go.mod

├── main.go

├── auth.go

├── auth_test.go

├── hash.go

└── hash_test.go

从示例我们可以看到,basic command类项目的repo下面只可构建出一个可执行文件,main函数放在main.go中,其他源文件也在repo根目录下,并同样放在main包中。

还是以repo托管在github上为例,如果这个repo的url为github.com/someuser/modname,那么我们可以通过下面命令安装这个command的可执行程序:

go install github.com/someuser/modname@latest

2.3 package with supporting packages

稍复杂或规模稍大的一些package类项目,会将很多功能分拆到supporting packages中,并且通常项目作者是不希望导出这些supporting

packages的,这样这些supporting packages便可以不作为暴露的API的一部分,后续重构和优化起来十分方便,对package的用户也是无感的。这样Go官方建议将这些supporting

packages放入[internal目录[8]](#参考资料)。

注:internal目录是[Go 1.4版本[9]](#参考资料)引入的机制,简单来说放在internal中的包是local的,不能导出到module之外,但module下的某些内部代码可以导入internal下的包。如今一般都会将internal放在项目的根目录下,所以项目下的所有代码都可以导入internal下的包。

下面是一个带有supporting packages的package类项目的目录布局示例:

project-root-directory/

├── go.mod

├── modname.go

├── modname_test.go

└── internal/

├── auth/

│ ├── auth.go

│ └── auth_test.go

└── hash/

├── hash.go

└── hash_test.go

modname.go或modname_test.go可以通过下面导入语句使用internal下面的包:

import "github.com/someuser/modname/internal/auth"

### 2.4 command with supporting packages

有了package with supporting packages的说明后,再来看command with supporting packages就更简单了,下面是一个示例:

project-root-directory/

├── go.mod

├── main.go

└── internal/

├── auth/

│ ├── auth.go

│ └── auth_test.go

└── hash/

├── hash.go

└── hash_test.go

和package with supporting packages不同的是,main.go使用的包名为main,这样Go编译器才能将其构建为command。

2.5 multiple packages

作为一个库项目,作者可能要暴露不止一个package,可能是多个packages。这不会给Go项目目录布局带来过多复杂性,我们只需多建立几个导出package的目录就ok了。下面是一个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

│ └── hash_test.go

└── internal/

└── trace/

├── trace.go

└── trace_test.go

我们看到这个示例在repo(以托管在github.com/user/modname下为例)顶层放置了多个导出包:

github.com/user/modname

github.com/user/modname/auth

github.com/user/modname/hash

并且顶层的auth目录下还有一个二级的导出包token,其导入路径为:

github.com/user/modname/auth/token

所有这些导出包的supporting

packages还是按惯例放在了internal目录下,比如:github.com/user/modname/internal/trace,这些包是local的,不能被该module之外的代码所依赖。


评论