Go - 项目布局/Layout - 学习/实践

1.应用场景

主要用于学习,以及采用合适的项目布局

2.学习/操作

1.文档阅读

05|标准先行:Go项目的布局标准是什么?-极客时间 -- 推荐阅读

this is not a standard Go project layout · Issue #117 · golang-standards/project-layout · GitHub

GitHub - golang-standards/project-layout: Standard Go Project Layout -- 并不是官方推荐的布局方式,简单说,就是太复杂了。

I always recommend anyone starting with Go to read

https://golang.org/doc/effective_go
https://github.com/golang/go/wiki/CodeReviewComments
https://github.com/golang/go/wiki
https://golang.org/ref/spec

2.整理输出

2.1 Go 语言创世项目源码

https://github.com/golang/go/releases/tag/go1

进入 Go 语言项目根目录后,我们使用 tree 命令来查看一下 Go 语言项目自身的最初源码结构布局,以 Go 1.3 版本为例,结果是这样的:

结构布局 -- 作为了解

$cd go // 进入Go语言项目根目录
$git checkout go1.3 // 切换到go 1.3版本
$tree -LF 1 ./src // 查看src目录下的结构布局
./src
├── all.bash*
├── clean.bash*
├── cmd/
├── make.bash*
├── Make.dist
├── pkg/
├── race.bash*
├── run.bash*
... ...
└── sudo.bash*

目前的布局

GitHub - golang/go: The Go programming language

2.2 演进过程/历史

这些早期的布局结构一直在不断地演化,简单来说可以归纳为下面三个比较重要的演进。

演进一:Go 1.4 版本删除 pkg 这一中间层目录并引入 internal 目录

演进二:Go1.6 版本增加 vendor 目录

演进三:Go 1.13 版本引入 go.mod 和 go.sum

详情参见:05|标准先行:Go项目的布局标准是什么?-极客时间

2.3 现在的 Go 项目的典型结构布局

Go项目通常可以分为两类:

可执行程序项目

库项目

1. Go 可执行程序项目的典型结构布局

可执行程序项目是以构建可执行程序为目的的项目,Go 社区针对这类 Go 项目所形成的典型结构布局是这样的:

单个module,但是输出多个可执行文件

$tree -F exe-layout 
exe-layout
├── 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/

如果是多个module,推荐拆分为多个code base.

当然如果你非要在一个代码仓库中存放多个 module,那么新版 Go 命令也提供了很好的支持。

比如下面代码仓库 multi-modules 下面有三个 module:mainmodule、module1 和 module2

$tree multi-modules
multi-modules
├── go.mod // mainmodule
├── module1
│   └── go.mod // module1
└── module2
    └── go.mod // module2

我们可以通过 git tag 名字来区分不同 module 的版本。其中 vX.Y.Z 形式的 tag 名字用于代码仓库下的 mainmodule;而 module1/vX.Y.Z 形式的 tag 名字用于指示 module1 的版本;同理,module2/vX.Y.Z 形式的 tag 名字用于指示 module2 版本。

如果 Go 可执行程序项目有一个且只有一个可执行程序要构建,那就比较好办了,我们可以将上面项目布局进行简化 --- 也是用得最多的

$tree -F -L 1 single-exe-layout
single-exe-layout
├── go.mod
├── internal/
├── main.go
├── pkg1/
├── pkg2/
└── vendor/

你可以看到,我们删除了 cmd 目录,将唯一的可执行程序的 main 包就放置在项目根目录下,而其他布局元素的功用不变。

2.4 Go 库项目的典型结构布局

Go 库项目仅对外暴露 Go 包,这类项目的典型布局形式是这样的:

$tree -F lib-layout 
lib-layout
├── go.mod
├── internal/
│   ├── pkga/
│   │   └── pkg_a.go
│   └── pkgb/
│       └── pkg_b.go
├── pkg1/
│   └── pkg1.go
└── pkg2/
    └── pkg2.go

我们看到,库类型项目相比于 Go 可执行程序项目的布局要简单一些。因为这类项目不需要构建可执行程序,所以去除了 cmd 目录。

而且,在这里,vendor 也不再是可选目录了。对于库类型项目而言,我们并不推荐在项目中放置 vendor 目录去缓存库自身的第三方依赖,库项目仅通过 go.mod 文件明确表述出该项目依赖的 module 或包以及版本要求就可以了。

Go 库项目的初衷是为了对外部(开源或组织内部公开)暴露 API,对于仅限项目内部使用而不想暴露到外部的包,可以放在项目顶层的 internal 目录下面。当然 internal 也可以有多个并存在于项目结构中的任一目录层级中,关键是项目结构设计人员要明确各级 internal 包的应用层次和范围。

对于有一个且仅有一个包的 Go 库项目来说,我们也可以将上面的布局做进一步简化,简化的布局如下所示:

$tree -L 1 -F single-pkg-lib-layout
single-pkg-lib-layout
├── feature1.go
├── feature2.go
├── go.mod
└── internal/

简化后,我们将这唯一包的所有源文件放置在项目的顶层目录下(比如上面的 feature1.go 和 feature2.go),其他布局元素位置和功用不变。

好了,现在我们已经了解完目前 Go 项目的典型结构布局了。不过呢,除了这些之外,还要注意一下早期 Go 可执行程序项目的经典布局,这个又有所不同。

2.5 注意早期 Go 可执行程序项目的典型布局

-- 主要是帮助我们进行开源项目的源码阅读

很多早期接纳 Go 语言的开发者所建立的 Go 可执行程序项目,深受 Go 创世项目 1.4 版本之前的布局影响,这些项目将所有可暴露到外面的 Go 包聚合在 pkg 目录下,就像前面 Go 1.3 版本中的布局那样,它们的典型布局结构是这样的:

$tree -L 3 -F early-project-layout
early-project-layout
└── exe-layout/
    ├── cmd/
    │   ├── app1/
    │   └── app2/
    ├── go.mod
    ├── internal/
    │   ├── pkga/
    │   └── pkgb/
    ├── pkg/
    │   ├── pkg1/
    │   └── pkg2/
    └── vendor/

我们看到,原本放在项目顶层目录下的 pkg1 和 pkg2 公共包被统一聚合到 pkg 目录下了。而且,这种早期 Go 可执行程序项目的典型布局在 Go 社区内部也不乏受众,很多新建的 Go 项目依然采用这样的项目布局。

所以,当你看到这样的布局也不要奇怪,并且在我的讲解后,你应该就明确在这样的布局下 pkg 目录所起到的“聚类”的作用了。不过,在这里还是建议你在创建新的 Go 项目时,优先采用前面的标准项目布局。

后续补充

...

3.问题/补充

1. 提议被拒

翻译

 

4.参考

参见上面文档列表

后续补充

...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值