Go 官方推荐的 Go 项目文件组织方式

刚开始接触 Go 的开发者大概都会遇到一个问题:我该如何组织我的 Go 项目?这种问题当然没有标准答案,不过 Go 官方下场,给了广大 Gopher 一个推荐模板。本文就来带大家一起来学习一下 Go 官方对于 Go 项目布局的指导原则。

本文以 Go 官方博客「Organizing a Go module」为基石进行讲解。

基础 Go 包

对于最基础的 Go 包,我们可以按照如下方式组织:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go

所有代码都位于项目的根目录下,这个项目由一个模块(module)组成,而这个模块又由一个包(package)组成。

假设这个项目会上传到 GitHub 仓库 github.com/someuser/modname 中,那么 go.mod 文件中的第一行(module line)应写为:

go 复制代码
module github.com/someuser/modname

modname.go 文件中的代码使用如下命令声明一个 modname 包:

go 复制代码
package modname

// ... package code here

在其他 Go 项目中,可以通过如下方式导入这个 Go 包:

go 复制代码
import "github.com/someuser/modname"

此外,一个 Go 包其实可以被拆分成多个文件:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth.go
  auth_test.go
  hash.go
  hash_test.go

这里所有 Go 文件都属于同一个包,即都会声明为 package modname

NOTE:

不知道你有没有注意,示例中的项目名称 project-root-directory 使用短横线来连接多个单词,而 Go 文件名则使用单词直接连写的形式 modname.go(而非 mod_name.go),这也是 Go 的一贯风格,虽然没有强制约定,但看多了社区中优秀的 Go 代码,你就会有所体会。

关于这点更加深入的探讨,你可以阅读我曾写过文章《Go 项目文件命名规范是什么?》来进行学习。

可执行的 Go 程序

Go 可执行程序(或命令行程序)一般根据复杂程度来构建目录,最小的 Go 程序可以仅定义一个包含 main 函数的 Go 文件,如果功能较多,可以将其拆分为多个文件:

bash 复制代码
project-root-directory/
  go.mod
  auth.go
  auth_test.go
  client.go
  main.go

main.go 中存放了 main 函数,不过这只是一个约定俗成的做法,你可以将其放到任何合法的 xxx.go 中。

当拆分为多个文件,如果 main.go 引用了其他文件中的代码,这个时候就不能简单的使用 go run main.go 来执行 Go 程序了,可以按照如下方式执行:

bash 复制代码
$ go run .

并且,用户可以使用以下命令将 Go 程序安装到自己的主机上:

bash 复制代码
$ go install github.com/someuser/modname@latest

更复杂的 Go 程序

如果一个 Go 包或者 Go 可执行程序更加复杂,则可以按照如下方式组织目录结构:

bash 复制代码
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 作为程序的入口,而非命名为 main.go

internal/ 目录下的所有包都不会被公开,其他程序无法引用。这是 Go 语言层面限制,强制的,不仅仅是约定俗成。这样,我们就可以随时随地的重构 internal/ 目录中的代码,而不必担心影响其他程序。

当然,modname.go 中可以导入 internal/ 目录下的包,因为它们本来就是一个程序。在 modname.go 中导入 auth 包:

go 复制代码
import "github.com/someuser/modname/internal/auth"

包含多个包的 Go 程序

我们知道,一个 Go 模块(module)可以由多个包(package)组成,而每个包都可以有自己的子目录。

我们可以组织成一个层次结构更深的 Go 程序:

bash 复制代码
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 包。

假设 go.mod 中声明 module 如下:

go 复制代码
module github.com/someuser/modname

modname.go 位于项目根目录,声明包为 modname,用户在使用时则可以按照如下方式导入此包:

go 复制代码
import "github.com/someuser/modname"

也可以按照如下方式导入子包:

go 复制代码
import "github.com/someuser/modname/auth"
import "github.com/someuser/modname/auth/token"
import "github.com/someuser/modname/hash"

注意:internal/trace 无法被外部程序导入。

包含多个可执行的 Go 程序

有时候,我们可能会在一个 Git 仓库中包含多个可执行的 Go 程序,可以按照如下方式组织目录:

bash 复制代码
project-root-directory/
  go.mod
  internal/
    ... shared internal packages
  prog1/
    main.go
  prog2/
    main.go

prog1/prog2/ 中分别有自己的 main.go 文件,即两个独立的可执行 Go 程序。

prog1/prog2/ 可以共享根目录中的其他包或子包。

我们可以按照如下方式分别安装二者到主机上:

bash 复制代码
$ go install github.com/someuser/modname/prog1@latest
$ go install github.com/someuser/modname/prog2@latest

其实,针对一个 Git 仓库中包含多个可执行的 Go 程序的情况,我们还有更好的做法来组织目录:

bash 复制代码
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

将可执行文件都放到 cmd/ 目录下也是约定俗成的做法。下次,当你见到 cmd/ 目录,就应该想到这个目录是干什么的。

现在可执行程序的安装命令如下:

bash 复制代码
$ go install github.com/someuser/modname/cmd/prog1@latest
$ go install github.com/someuser/modname/cmd/prog2@latest

服务器项目

其实,我们在用 Go 编写程序时,大多数是用来实现 Go Web Server 程序,这也是 Go 最常见的使用场景,Go 是实现服务器的通用语言选择。

比如 Kubernetes 项目,其最核心的组件也是 Server 程序。

通常,我们可以按照如下方式来组织 Go 服务器项目:

bash 复制代码
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 Web 开发经验,是不是很熟悉这个目录结构?

这已经到了广大 Gopher 最熟悉的领域,接下来,我就不过多讲解了,你可以继续阅读我写的另一篇文章《如何设计一个优秀的 Go Web 项目目录结构》,里面介绍了开源项目 github.com/golang-stan... 对 Go Web 项目组织的最佳实践。

总结

我们学习了 Go 官方推荐的项目布局的指导原则,现在我们对于 Go 包和 Go 可执行程序的目录组织方式,都有了较为清晰的认知。

此外,我们还顺便学习了如何安装 Go 可以执行程序,可以通过 go install xxx 来安装。

对于广大 Gopher,接触最多的应该是 Go Web 服务器的开发。对于 Go Web 项目,不仅 Go 官方给出了指导原则,我们还可以参考 project-layout 项目学习一个大型的 Go 项目是如何布局的。

希望此文能对你有所启发。

联系我

相关推荐
许苑向上42 分钟前
Spring Boot 自动装配底层源码实现详解
java·spring boot·后端
再学一点就睡1 小时前
手写 Promise 静态方法:从原理到实现
前端·javascript·面试
再学一点就睡2 小时前
前端必会:Promise 全解析,从原理到实战
前端·javascript·面试
超级小忍3 小时前
深入浅出:在 Spring Boot 中构建实时应用 - 全面掌握 WebSocket
spring boot·后端·websocket
没有bug.的程序员4 小时前
《Spring Security源码深度剖析:Filter链与权限控制模型》
java·后端·spring·security·filter·权限控制
无责任此方_修行中4 小时前
不止是 AI 热潮:AWS 2025 技术峰会带给我的思考
后端·架构·aws
lang201509284 小时前
Apache Ignite 与 Spring Boot 集成
spring boot·后端·apache·ignite
Asthenia04124 小时前
深入剖析 Spring Boot 请求处理链路与 Servlet 的本质
后端
旧时光巷4 小时前
【Flask 基础 ①】 | 路由、参数与模板渲染
后端·python·零基础·flask·web·模板渲染·路由系统
小醉你真好5 小时前
Spring Boot 数据源配置中为什么可以不用写 driver-class-name
spring boot·后端·源代码管理