中等规模的Golang项目是否有公认的标准目录结构?
中等规模的Golang项目是否有公认的标准目录结构? 我正在研究是否应该为我的新开源应用程序遵循一个标准的目录结构。我这样做是为了让人们能够轻松理解代码并轻松做出贡献。
这是一个中等规模的Web应用程序,由一个用Go编写的JSON API后端和一个用Typescript编写的Next.js前端组成。
根据Go项目结构最佳实践,最常见的选项是:
- 对于小型应用:扁平结构。将所有源文件放在顶层。
- 对于中型/大型应用:将文件拆分为语义组。每个组目录位于顶层。
- 对于大型或“较旧”的应用:“传统”结构,我认为它指的是go-standards/project-layout。顶层由按技术组划分的子目录组成(“pkg”用于公开可消费的源代码,“internal”仅用于内部可消费的源代码),而不是按语义组划分。在pkg或internal子目录中,仍然可以有语义组。
问题1: 对于我的中型应用程序,我应该选择哪一个?Go社区对于选择哪个选项是否有广泛的共识?
一位朋友是go-standards/project-layout的大力倡导者。据他说,这是Go的标准,允许每个Go开发者立即上手并轻松理解结构。
问题2: go-standards/project-layout是否真的是被广泛接受的金标准?
当我查看实际的Go仓库时,我实际上并没有看到这是最被接受的布局。例如,Hugo、HashiCorp Vault和Terraform似乎都使用布局#2。除了Kubernetes之外,我很难找到使用go-standards/project-layout的例子。我错过了什么吗?
问题3: Go项目结构最佳实践说布局#3适用于“较旧”的应用程序,并且他/她预计随着Go模块开始变得更加普遍,Go社区将逐渐放弃这种布局。你对此有何看法?
更多关于中等规模的Golang项目是否有公认的标准目录结构?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
一些涉及理论知识的资源: https://peter.bourgon.org/go-for-industrial-programming/#structuring-your-code-and-repository

包作为层次,而非分组
Go 语言中的包与其他语言不同。了解如何在应用程序增长过程中将其构建为层次结构。
更多关于中等规模的Golang项目是否有公认的标准目录结构?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好,Hong,
希望我能回答这个问题。根据你的要求,我认为第二个选项会更好。如果你想参考,我这里有一个例子:https://github.com/MrWormHole/faceit-case-interview
Go 的维护者和谷歌开源社区通常采用第三个选项,但这被认为是高度基于个人观点的。就像简洁与冗长的争论一样,我经常看到人们将简洁的讨论推向极端,试图用单个字母重命名所有东西,在我看来这并不好。如果你能遵循第三个选项,对于 Go 社区内部的人来说可能是有益的,因为遵循 internal、example、pkg、cmd 这样的文件夹结构有一些好处。
然而,如果你正在构建一个后端 Web API,我很难看出这些好处,因为你是该 API 的唯一最终用户。你的端点文档实际上将变得比你如何设计文件夹结构重要得多,因为我们需要了解 API 规范,而不是你的文件夹目录。你提到的第二个选项也为 Go 社区之外的人提供了更清晰的视图,这可能很方便。
显然,Go 中的许多项目维护者都是多语言开发者,所以他们可能更倾向于选择第二个选项而不是第三个,以使事情更符合他们的习惯。但如果你打算盲目地遵循 Java 的文件夹结构,那就不要这样做,正如你所说,选择第三个选项。
我不同意第三个选项是过时的方式并且将来不会被使用的说法。这只是一种特定的组织结构方式,不是每个人都喜欢,但这将取决于你实际构建的程序是什么,以及你对 vendor、example、internal、pkg 这些东西的适应程度。
如果你认为你正在构建可重用的大型企业级项目,就选择第三个选项。如果你认为你将是在单个项目或类似项目中唯一使用它的人,就选择第二个选项。如果是像非常小的工具库这样的东西,就选择第三个选项。
问题 1: 我应该为我的中型应用选择哪一个?Go 社区对选择哪个选项有广泛的共识吗?
根据你的仓库目的,选项 2 和选项 3 都可以。以下是我经历过的一些简化案例:
- 仅库包 - 选择选项 2,因为它简化了导入语句。
- 应用但包含可导入的库包 - 选择选项 3,使用
pkg/目录存放库包,app/目录存放程序。 - 仅应用 - 选择选项 2 或选项 3,取决于它有多大,以及我是否需要标准化和自动化构建过程。
关键是要关注你的客户(Go 开发者)导入你的包有多容易。
另外,对于目的 2,如果你有像目的 1 那样的库包仓库,也更容易将一个包向上游推送到那里。
据他说,这是 Go 的标准,允许每个 Go 开发者立即上手并轻松理解结构。
问题 2: go-standards/project-layout 是否是广泛接受的金标准?
这是一种通用实践,而不是标准/规则。其文档中已写明。由于大多数 Go 开发者都了解那种布局,所以它可以节省理解你仓库的时间(也就是说,我不需要分析你自定义的布局结构)。此外,某些部分符合 Go 编译器的要求,比如 internal/ 目录。
Go 和 go-standards/project-layout 并没有限制你或为你如何布局项目设定金科玉律。它们是从先前开发者遇到的各种问题中总结出来的经验教训。我的建议是将其作为参考或灵感的来源。然后,随着时间的推移不断改进你的仓库。
问题 3: Go 项目结构最佳实践说布局 #3 适用于“较旧”的应用,并且他/她预计随着 Go 模块开始变得更加普遍,Go 社区将逐渐放弃这种布局。你对此有何看法?
荒谬。Go 模块对布局结构模式零影响。Go 模块所做的是将 Go 包从 GOPATH 路径限制中解放出来,同时解决了一些依赖管理问题。你仍然需要从一开始就规划和设计你的项目布局。
你唯一需要严格、放慢速度并小心处理的是那些供其他 Go 开发者导入的公共 API 包。原因如下:
- 你不想更改子包的目录路径,这会导致客户端的导入语句失败。
- 如果你想删除一个已发布的 API 包,你需要发布弃用通知、下架通知等。大部分资源都花在与客户的沟通上。
- 通常你需要发布主版本(那些
v2、v3等)的发布。这本身就有其挑战性。
除此之外,任何内部布局完全取决于你作为维护者的选择。
我这样做是为了让人们能够轻松理解代码并轻松做出贡献。
通常,拥有以下所有内容就足够了:
- 一份清晰的文档,说明你希望他人如何为你的仓库做贡献。
- 源代码符合 Effective Go。
- [最好能做到] 使你的开发流程/自动化构建工具无缝衔接(也就是说,我不需要摆弄你的构建工具或尝试在我的本地机器上安装大量依赖项)。
对于中等规模的Golang项目,确实存在一些被广泛讨论的目录结构模式,但并没有一个官方强制执行的“金标准”。以下是针对您问题的具体分析:
问题1:中型项目应选择哪种结构?
对于中等规模的Web应用程序,选项2(按语义分组)通常是更合适的选择。这种结构更直观,便于理解和维护。
示例结构:
myapp/
├── cmd/
│ └── api/
│ └── main.go # 应用入口
├── internal/
│ ├── handlers/ # HTTP处理器
│ ├── services/ # 业务逻辑
│ ├── repositories/ # 数据访问层
│ └── models/ # 数据模型
├── pkg/
│ ├── middleware/ # 可复用的中间件
│ └── utils/ # 工具函数
├── web/ # Next.js前端
│ ├── package.json
│ └── src/
├── go.mod
├── go.sum
└── README.md
这种结构的优势在于:
- 按业务功能组织,便于新贡献者理解
- 避免了过度复杂的嵌套
- 与Go模块系统配合良好
问题2:go-standards/project-layout是否是金标准?
go-standards/project-layout并非官方标准,也没有被Go团队正式认可。它只是一个社区维护的参考布局。
实际情况是:
- 大多数流行的Go项目(如Docker、Terraform、Prometheus)并没有严格遵循该布局
- 该布局更适合大型、复杂的库或框架项目
- 对于大多数应用项目来说,它可能过于复杂
示例:更常见的实际项目结构(如Terraform风格)
terraform/
├── command/ # CLI命令
├── backend/ # 后端接口
├── configs/ # 配置处理
├── internal/ # 内部包
│ ├── command/
│ ├── backend/
│ └── tfdiags/
├── vendor/ # 依赖(如果需要)
└── main.go
问题3:关于传统布局的未来
Go模块的普及确实改变了项目结构的考量因素:
- 模块化简化了依赖管理:
// go.mod 示例
module github.com/yourname/myapp
go 1.21
require (
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.10.9
)
- internal目录的重要性增加:
// internal/auth/auth.go
package auth
import "errors"
// 这个包只能被主模块内部使用
func ValidateToken(token string) error {
if token == "" {
return errors.New("token is empty")
}
return nil
}
- 更扁平的结构成为趋势:
// 现代中等项目常见布局
myapp/
├── api/ # API定义和客户端
├── cmd/ # 可执行文件入口
├── internal/ # 私有应用代码
│ ├── user/ # 用户相关逻辑
│ ├── order/ # 订单相关逻辑
│ └── payment/ # 支付相关逻辑
├── pkg/ # 公共库代码(可选)
├── migrations/ # 数据库迁移
├── tests/ # 集成测试
├── go.mod
└── Dockerfile
实际建议:对于您的中型Web应用,采用语义分组(选项2)结合cmd/、internal/和pkg/(如果需要)的混合结构最为实用。关键是保持一致性,并在README中明确说明结构设计。

