本文将描述用于构建 Golang 应用程序的常见方法。Golang 没有一个官方的项目结构指南,所以它是一个有观点的 Go 应用结构指南。
Go 是一门神奇的语言,它简单易学,开箱即用,给你提供了很多工具。大多数开始学习 Golang 的 Gophers 都有一个共同点,由于缺乏官方指南,他们很难弄清楚如何构造应用程序。构造 Golang 应用程序的主要方法是开始时要简单,但要有发展的灵活性。
对于目录结构或如何以特定方式组织 Go 项目文件,没有严格的规则。这很糟糕,因为它很容易造成混乱,也很好,因为项目的结构可以根据开发者的口味来构建。
好的结构是在整个项目中保持一致。没有什么比来到代码库,看到一部分代码以一种方式完成,另一部分以另一种方式完成更糟糕的了。特别是当你不是原作者,而你刚刚来到新的代码库,你不知道你应该遵循哪种结构。好的代码结构必须是容易理解和浏览的,而且应该是有意义的。好的代码结构将使你作为一个程序员的生活变得轻松和有趣。
Go 开发人员通常遵循某些模式来布置他们项目中的文件和目录。他们通常遵循这些模式,因为这对他们和他们的同伴来说是可行的。在我看来,组织应该为他们所有的 Golang 应用项目遵循相同的结构,因为这样可以使开发人员更有生产力。
在 Go 中构建项目文件的结构
组织 Go 项目的更好方法是将相关的 Go 代码文件放到项目主目录下的子目录中,这样项目的其他部分就能找到服务 API 并使用它。
把所有的源文件放在同一个目录下并不是一个好主意,因为它使代码文件变得杂乱无章,结果是你要花大部分时间去寻找正确的文件。大多数有经验的开发者都知道这一点。
为了演示一个项目的布局,我们将创建一个名为 "go-layout "的演示项目。
这是示例项目类型的树状结构。下面我们将解释每个文件夹的作用:
/cmd
该文件夹将包含这个项目的主要应用程序。
cmd 文件夹中可能有不止一个应用程序,每个应用程序都应该有自己的文件夹,所以路径是 cmd -> applicationNameFolder。
每个应用程序的目录名称应与你想拥有的可执行文件的名称一致,例如,cmd/myapp。
重要的是,我们不要把大量的代码放在应用程序目录中。如果你认为这些代码可以被导入并用于其他项目,最好将这些代码放在 pkg 目录中。
通常的做法是有一个小的主函数来导入和调用 /internal 和 pkg 目录下的代码。
请查看一些用 Golang 编写的成功项目。
例子:
- https://github.com/kubernetes/kubernetes/tree/master/cmd
- https://github.com/ethereum/go-ethereum/tree/master/cmd
- https://github.com/influxdata/influxdb/tree/master/cmd
/api
这个文件夹可以有 OpenAPI/Swagger 规格、JSON模式文件和协议定义文件。
例子:
• https://github.com/moby/moby/tree/master/api
/build
这个文件夹可以有与打包和持续集成文件相关的文件夹或文件,如 Docker 和脚本。
把你的 CI 配置和脚本放在 /build/ci 目录中。
例子:
• https://github.com/cockroachdb/cockroach/tree/master/build
/configs
它可以有配置文件模板或默认配置。
/examples
你可以放置你的 应用或公共类库的例子。
例子:
- https://github.com/hashicorp/packer/tree/main/examples
- https://github.com/docker-slim/docker-slim/tree/master/examples
/internal
私人应用程序和库的代码。这是你不想让别人导入他们的应用程序或库中的代码。请注意,这种布局模式是由 Go 编译器本身强制执行的。请注意,你并不局限于顶级的内部目录。你可以在你的项目树的任何一级有一个以上的内部目录。
你可以选择给你的内部包添加一些额外的结构,以分离你的共享和非共享的内部代码。这不是必须的(特别是对于较小的项目),但有视觉线索显示预期的包的用途是不错的。
你的实际应用代码可以放在 /internal/app 目录下(例如,/internal/app/myapp),而那些应用共享的代码放在 /internal/pkg 目录下(例如,/internal/pkg/myprivlib)。
文件夹 /internal/pkg 包含了不特定于此应用程序的包。如果这些包能提供实用性,理论上可以提取到一个独立的公共包中。
例子:
- https://github.com/influxdata/influxdb/tree/master/internal
- https://github.com/hashicorp/terraform/tree/master/internal
/pkg
可以被外部应用程序使用的库代码(例如:/pkg/mypubliclib)。其他项目会导入这些库,期望它们能够工作,所以在你把东西放在这里之前要仔细检查。
请注意,内部目录是确保你的私有包不能被导入的更好方法,因为它是由 Go 强制执行的。
如果你的应用项目非常小,而且额外的嵌套并没有增加多少价值,不使用 /pkg 目录是可以的。但当你的项目越来越大,你的根目录变得繁忙时,你就需要考虑这个问题了。
例子:
- https://github.com/kubernetes/kubernetes/tree/master/pkg
- https://github.com/helm/helm/tree/master/pkg
/third_party
外部辅助工具、分叉代码和其他第三方工具(例如,Swagger UI)。
/net
网络应用程序专用组件:静态网络资产、服务器端模板和SPA。
/http
你可能想通过几种通信手段暴露你的应用程序,如 rest-api 和 grpc。
这样你就可以通过创建一个单独的目录,如 /http/rest 或 /http/grpc,将每种类型的通信层单独分开。