深入了解Go项目标准目录布局
很多的时候,我们开发一个简单的Go项目的时候并不需要纠结于项目的的目录布局,因为我们会将所有go源码文件扔在项目的根目录中,就像下面这样:
demo ├── main.go ├── model.go └── service.go
但当我们的项目变得复杂的时候,我们就需要好好思考怎么组织我们的项目了,这时候你可能会想,用官方的Go项目目录布局就好了,然而Go的官方并没有给出一个标准的Go项目标准目录布局。
目前,Go社区开发者比较推荐的是project-layout项目中给出的目录布局,如:
demo ├── api ├── assets ├── build ├── cmd ├── configs ├── deployments ├── docs ├── examples ├── githooks ├── init ├── internal ├── pkg ├── scripts ├── test ├── third_party ├── tools ├── vendor ├── web └── website
然而官方并不推荐这种项目布局方式,下面是Go团队开发leader Russ Cox对project-layout目录的意见:
大体上,我们可以将Go的项目分为两类,应用项目与库项目。
应用项目:即包含可执行文件的项目,项目开发后,需要编译成可执行文件并上线运行。
库项目:库文件一般用于暴露Api,被其他项目通过import关键字引入。
应用项目与库项目的目录布局方式稍有差异。
应用项目
对于应用项目,其目录布局方式我们可以参考Go项目自身,以及一些使用Go语言开发的优秀开源项目(如Docker,Kubernetes,etcd)等,通过研究这些项目的目录布局,我们可以抽象出以下标准目录:
demo ├── cmd │ ├── app1 │ │ └── main.go │ └── app2 │ └── main.go ├── go.mod ├── go.sum ├── internal │ ├── pkga │ │ └── pkg_a.go │ └── pkgb │ └── pkg_b.go ├── pkg1 │ └── pkg1.go ├── pkg2 │ └── pkg2.go └── vendor
cmd
可执行文件目录,如果一个项目有多个可执行文件,可以放在不同的子目录中,如上面例子中的app1和app2目录。
internal
项目内部私有代码,其他项目引入时会报错。
pkgN:
注意,这里并不是说一定要pkg为前缀来命名,你可以对取任意符合包命名规范的名称,比如service,model等
即上面示例中的pkg1与pkg2,pkgN包与internal包一样存放项目的依赖代码,但存储在这里的代码,可以被其项目引入。
vendor
这个目录用于存储项目的依赖包,但由于现今Go项目都是使用go module进行依赖管理,因此这个目录是可省略的。
如果我们的项目只有一个可执行文件,可以将main.go文件直接写在根目录,而vendor可以省略,因此,项目目录可以进一步精简为:
demo ├── go.mod ├── go.sum ├── internal │ ├── pkga │ │ └── pkg_a.go │ └── pkgb │ └── pkg_b.go ├── main.go ├── pkg1 │ └── pkg1.go └── pkg2 └── pkg2.go
库项目
库项目不需要可执行文件,相比于应用项目,省略了cmd目录,其标准目录下:
demo ├── go.mod ├── go.sum ├── internal │ ├── pkga │ │ └── pkg_a.go │ └── pkgb │ └── pkg_b.go ├── pkg1 │ └── pkg1.go └── pkg2 └── pkg2.go
如果我们的库项目比较简单,可以采用平铺式项目布局,即将所有的功能写在一个包里,所有源码目录放在根目录下,如:
demo ├── feature1.go ├── feature2.go ├── feature3.go ├── go.mod └── go.sum
平铺式的项目布局非常适合简单的库项目,事实上,很多复杂的库项目也会将大量的代码放在根目录下,因为这样,其他项目使用import引入项目的包时,路径会比较短,比如我们有一个结构体A,放在pkg1包下与放在根目录下,其引路径分别是:
import "demo" import "demo/pkg1"
小结
好了,阅读到这里,想必你已经了解了Go应用项目与库项目目录布局的不同了吧。
不过,由于Go官方项目指定的项目布局,因此上述的几种项目目录都是社区开发的约定俗成,并不具备强制性,你也可以定制自己的Go项目目录布局,不过我想,使用社区开发都遵循的目录规范,还是有助于降低团队开发人员的认知与交流成本的。