hello world

stay foolish, stay hungry

golang 项目结构

常见的golang代码布局方式通常有扁平化布局和模块化布局。

扁平化布局

扁平化布局很简单,代码之间不分层,所有代码放在同一个目录。这种布局风格适合比较简单的项目。我们见到的工具包通常就是这种布局,例如 errors 项目的结构:

project
|
|- bench_test.go
|
|-errors.go
|
|-errors_test.go
|
|-example_test.go
|
|-format_test.go
|
|-go113.go
|
|-go113_test.go
|
|-json_test.go
|
|-stack.go
|
|-stack_test.go

值得注意的是,工具包中尽量不要有第三方依赖,否则,维护工具包中的第三方依赖会很麻烦,也很容易给其他项目带来版本冲突等问题。

模块化布局

项目稍微复杂一些之后,扁平化布局会显得比较混乱,我们通常会按功能将代码放到不同的目录中,也就是模块化的布局方式。从 1.14 版本之后,官方推荐使用 [Go Modules](https://blog.golang.org/using-go-modules) 管理依赖,项目一般会由 cmdinternalpkgvendor 等目录组成,当然这不是官方建议的项目结构,只是目前比较常见的布局方式。

project
|
|-- cmd/
|    |
|    |-- app1/
|    |    |
|    |    |- main.go
|    |    |
|    |    |- handler/
|    |
|    |-- app2/
|        |
|        |- main.go
|        |
|        |- handler/
|
|-- internal/
|    |
|    |-- pkg
|    |   |
|    |   |-- data
|    |   |
|    |   |-- log
|    |
|    |-- router
|    |
|    |-- service
|
|-- pkg/
|    |
|    |-- kit
|
|-- vendor/
|
|-- go.mod
|
|-- go.sum

cmd/

cmd 目录项目中的可执行程序的入口,主要放置 main() 函数,每个可执行程序对应一个子文件夹,文件夹应以程序的名称命名,可以在名字后面加字母 d,表示程序将以守护进程的方式运行。应用程序中必须包含一个 main() 函数的源文件,即 demo 中的 main.go,文件名字最好与程序名字一致。cmd 包可能会导入 internalpkgvendor 包中的代码。

internal/

internal 目录是项目私有的代码。 1.4 版本新增了 Internal packages 特性,internal 目录中的代码只能被 internal 目录的父目录下的子目录引用,举个例子 .../a/b/c/internal/d/e/f 仅仅可以被 .../a/b/c 下的目录导入,.../a/b/g 则不允许。除了 internal 根目录之外,也可以在任何目录下创建 internal 目录。

pkg/

放在 pkg 目录下的代码可以被其他项目直接导入。可以理解为 internal 目录下的代码是项目的私有代码,而 pkg 目录下的代码是开放的代码。如果项目中不包含公共的代码,则可以不需要 pkg 包。而且如果项目足够小,或者项目只是一个工具包,也完全不需要 pkg 包。

vendor/

存放项目依赖的目录,通常是依赖管理工具自己来维护。例如, go mod vendor 命令会将项目的依赖放到 vendor 目录。

其他目录

不同类型的项目通常还包括其他的一些目录,比如服务型项目通常会有一个 api 目录,web 项目通常会有一个 web 目录以存放静态资源,工具包类型的项目通常会包含一个 examples 目录。

总结

这篇文章简单的描述了常见的 golang 项目布局方式,实际项目中,不一定要按照这两种方式来组织代码结构,一切以实际项目为准。先让代码能跑起来, 再试图让它变得更好,最后再试着让它变得更快。