【译文】原文地址
说明:本文最初发表在martinheinz.dev
对我来说,开始一个新项目时最大的麻烦就是试图把项目配置的“完美”。我总是尝试使用最优的目录结构,这样一切都容易找到,以及导入工作也方便,设置所有的命令,这样就可以达到一键/一条命令完成所需操作,比如找到最好的工具、格式化、测试框架等。
这个列表还在增加,远没有达到我满意的配置...除了本文介绍的终极最优Glang配置。
说明:这个配置工作的这么好,部分原因基于已有项目,如项目1和项目2。
我的项目仓库地址:https://github.com/MartinHeinz/go-project-blueprint
目录结构
首先,让我们回顾下我们项目的目录结构。包含几个顶级文件和4个目录:
- pkg-先从简单的开始-pkg是一个Go package包含唯一的全局版本字符串。它是根据提交hash计算出来替换实际版本的。
*config-这是一个配置目录,包含必要的环境变量文件。任何文件类型都可能被使用,但我推荐使用yaml文件,其可读性好。
*build-这个目录包含构建和测试应用程序的所有的shell脚本以及为代码分析工具所生产的报告。
*cmd-存放源代码目录。按照惯例,源代码目录命名为cmd,里面包含一个根据项目名称命名的目录,在本文中是blueprint。接下来,在这个目录中包含一个main.go文件用来运行整个应用程序的,还有其他划分成模块的源文件。
备注:根据一些反馈,我发现很多人喜欢使用internal和pkg目录来存放源代码。我个人发现这没必要也有点多余,因此我把所有源文件都存放在cmd目录,然后每个模块都包含独立的目录。
除目录外,还有很多文件,我们将在以下各节中讨论这些文件。
Go Module可实现完美的依赖关系管理
Go项目使用多种依赖管理策略。但是,从1.11版开始,Go就有官方的依赖项管理解决方案。我们所有的依赖项都在go.mod文件中列出,该文件可以在根目录中找到。看起来可能是这样:
module github.com/MartinHeinz/go-project-blueprint
go 1.12
require (
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.4.0
)
实际源代码和配置
下面终于讲源代码了。如前所述,源代码会划分成包,每个包都对应cmd中一子目录。每个子目录包含各自的源文件和测试文件,如下:
./cmd/
└── blueprint
├── apis <- Package
│ ├── apis_test.go
│ ├── user.go
│ └── user_test.go
├── daos <- Package
│ ├── user.go
│ └── user_test.go
├── services <- Package
│ ├── user.go
│ └── user_test.go
├── config <- Package
│ └── config.go
└── main.go
以上目录结构有助于可读性和可维护性,因为代码被划分成合理的包,便于浏览。至于配置,在本项目中我使用 Viper,一个Go配置库,可以处理各种格式、命令行参数和环境变量等。
那么这里如何使用Viper呢?我们可以看下config包:
var Config appConfig
type appConfig struct {
// Example Variable, which is loaded in LoadConfig function
ConfigVar string
}
// LoadConfig loads config from files
func LoadConfig(configPaths ...string) error {
v := viper.New()
v.SetConfigName("example") // <- name of config file
v.SetConfigType("yaml")
v.SetEnvPrefix("blueprint")
v.AutomaticEnv()
for _, path := range configPaths {
v.AddConfigPath(path) // <- // path to look for the config file in
}
if err := v.ReadInConfig(); err != nil {
return fmt.Errorf("failed to read the configuration file: %s", err)
}
return v.Unmarshal(&Config)
}
该package只含一个文件。其中声明了一个struct存放所有的config变量以及一个LoadConfig函数,加载配置。该函数接收配置文件,在我们的项目中将使用config目录所在路径,包含yaml文件。然后我们怎么使用呢?我们先在main.go中执行:
if err := config.LoadConfig("./config"); err != nil {
panic(fmt.Errorf("invalid application configuration: %s", err))
}
上面的代码一般在应用程序中,启动加载配置的作用。
简单快速的测试
介绍完源代码结构后,第二重要的事情就是质量测试了。为了能写出很多好的测试,需要配置下使写测试更简单。我们可以使用Makefile添加test目标,将搜集和执行cmd目录中所有的测试用例。这些测试用例也会缓存,只有存在变更才会再执行。这点很重要,因为如果测试太慢您将终止运行和维护它们。除了单元测试,make test也可以帮助维护代码质量,因为每次执行测试都会运行gofmt和go vet。go fmt会强制格式化代码而go vet根据启发式算法会发现任何可疑代码构造。示例输出:
foo@bar:~$ make test
Running tests:
ok github.com/MartinHeinz/go-project-blueprint/cmd/blueprint (cached)
? github.com/MartinHeinz/go-project-blueprint/cmd/blueprint/config [no test files]
? github.com/MartinHeinz/go-project-blueprint/pkg [no test files]
Checking gofmt: FAIL - the following files need to be gofmt'ed:
cmd/blueprint/main.go
Checking go vet: FAIL
# github.com/MartinHeinz/go-project-blueprint/cmd/blueprint
cmd/blueprint/main.go:19:7: assignment copies lock value to l: sync.Mutex
Makefile:157: recipe for target 'test' failed
make: *** [test] Error 1
有疑问加站长微信联系(非本文作者)