Golang 项目结构
# Golang 项目结构
这一部分的内容主要来自于 github 的高星项目:golang-standards/project-layout (opens new window) 通过这个我们可以大概的了解到在 Go 中一些约定俗成的目录含义,虽然这些不是强制性的,但是如果有去看官方的源码或者是一些知名的项目可以发现大多都是这么命名的,所以我们最好和社区保持一致,大家保持同样的语言。
以go-gin-api为例
.
├── assets
│ ├── bootstrap
│ │ ├── css
│ │ ├── fonts
│ │ ├── images
│ │ │ └── users
│ │ └── js
│ └── templates
│ ├── admin
│ ├── authorized
│ ├── configinfo
│ ├── cron_task
│ ├── dashboard
│ ├── generator
│ ├── index
│ ├── install
│ ├── menu
│ └── tool
├── cmd
│ ├── gormgen
│ │ └── pkg
│ ├── handlergen
│ ├── mfmt
│ └── mysqlmd
│ └── mysql
├── configs
├── deployments
│ ├── loki
│ └── prometheus
├── docs
├── internal
│ ├── alert
│ ├── api
│ │ ├── admin
│ │ ├── authorized
│ │ ├── config
│ │ ├── cron
│ │ ├── helper
│ │ ├── menu
│ │ └── tool
│ ├── code
│ ├── graph
│ │ ├── generated
│ │ ├── handler
│ │ ├── model
│ │ ├── resolvers
│ │ │ └── generated
│ │ └── schemas
│ ├── metrics
│ ├── pkg
│ │ ├── core
│ │ ├── password
│ │ └── validation
│ ├── proposal
│ │ └── tablesqls
│ ├── render
│ │ ├── admin
│ │ ├── authorized
│ │ ├── config
│ │ ├── cron
│ │ ├── dashboard
│ │ ├── generator
│ │ ├── index
│ │ ├── install
│ │ ├── tool
│ │ └── upgrade
│ ├── repository
│ │ ├── cron
│ │ ├── mysql
│ │ │ ├── admin
│ │ │ ├── admin_menu
│ │ │ ├── authorized
│ │ │ ├── authorized_api
│ │ │ ├── cron_task
│ │ │ ├── menu
│ │ │ └── menu_action
│ │ ├── redis
│ │ └── socket
│ ├── router
│ │ └── interceptor
│ ├── services
│ │ ├── admin
│ │ ├── authorized
│ │ ├── cron
│ │ └── menu
│ └── websocket
│ └── sysmessage
├── logs
├── pkg
│ ├── aes
│ ├── browser
│ ├── color
│ ├── ddm
│ ├── debugs
│ ├── env
│ ├── errors
│ ├── file
│ ├── hash
│ ├── httpclient
│ ├── logger
│ ├── mail
│ ├── rsa
│ ├── shutdown
│ ├── signature
│ ├── timeutil
│ ├── trace
│ └── urltable
└── scripts
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# /cmd
我们一般采用 /cmd/[appname]/main.go
的形式进行组织
- 首先 cmd 目录下一般是项目的主干目录
- 这个目录下的文件不应该有太多的代码,不应该包含业务逻辑
- main.go 当中主要做的事情就是负责程序的生命周期,服务所需资源的依赖注入等,其中依赖注入一般而言我们会使用一个依赖注入框架,这个主要看复杂程度,后续会有一篇文章单独介绍这个
# /internal
internal 目录下的包,不允许被其他项目中进行导入,这是在 Go 1.4 当中引入的 feature,会在编译时执行
- 所以我们一般会把项目文件夹放置到 internal 当中,例如
/internal/app
- 如果是可以被其他项目导入的包我们一般会放到 pkg 目录下
- 如果是我们项目内部进行共享的包,而不期望外部共享,我们可以放到
/internal/pkg
当中 - 注意 internal 目录的限制并不局限于顶级目录,在任何目录当中都是生效的
# /pkg
一般而言,我们在 pkg 目录下放置可以被外部程序安全导入的包,对于不应该被外部程序依赖的包我们应该放置到 internal
目录下, internal
目录会有编译器进行强制验证
- pkg 目录下的包一般会按照功能进行区分,例如
/pkg/cache
、/pkg/conf
等 - 如果你的目录结构比较简单,内容也比较少,其实也可以不使用
pkg
目录,直接把上面的这些包放在最上层即可 - 一般而言我们应用程序 app 在最外层会包含很多文件,例如
.gitlab-ci.yml
makefile
.gitignore
等等,这种时候顶层目录会很多并且会有点杂乱,建议还是放到/pkg
目录比较好
# /config(s)
为什么加个(s) 是课上讲的还有参考材料中很多都叫 configs 但是我们习惯使用 config 但是含义上都是一样的 这里面一般放置配置文件文件和默认模板
# /test
额外的外部测试应用程序和测试数据。一般会放测试一些辅助方法和测试数据
# build
打包和持续集成所需的文件。
- build/ci:存放持续集成的配置和脚本,如果持续集成平台对配置文件有路径要求,则可将其 link 到指定位置。
- build/package:存放 AMI、Docker、系统包(deb、rpm、pkg)的配置和脚本等。
例子:
- https://github.com/cockroachdb/cockroach/tree/master/build
# configs
配置文件模板或默认配置。
# deployments
IaaS,PaaS,系统和容器编排部署配置和模板(docker-compose,kubernetes/helm,mesos,terraform,bosh)。请注意,在某些存储库中(尤其是使用 kubernetes 部署的应用程序),该目录的名字是 /deploy。
# init
系统初始化(systemd、upstart、sysv)和进程管理(runit、supervisord)配置。
# scripts
用于执行各种构建,安装,分析等操作的脚本。
这些脚本使根级别的 Makefile 变得更小更简单,例如:https://github.com/hashicorp/terraform/blob/master/Makefile。
# test
外部测试应用程序和测试数据。随时根据需要构建 /test
目录。对于较大的项目,有一个数据子目录更好一些。例如,如果需要 Go 忽略目录中的内容,则可以使用 /test/data
或 /test/testdata
这样的目录名字。请注意,Go 还将忽略以“.”或“_”开头的目录或文件,因此可以更具灵活性的来命名测试数据目录。
# assets
项目中使用的其他资源(图像、logo 等)。
# docs
设计和用户文档(除了 godoc 生成的文档)。
# examples
应用程序或公共库的示例程序。
# githooks
Git 钩子。
# third_party
外部辅助工具,fork 的代码和其他第三方工具(例如:Swagger UI)。
# tools
此项目的支持工具。请注意,这些工具可以从 /pkg
和 /internal
目录导入代码。
# 应该避免的坏习惯
# /src
一般而言,在 Go 项目当中不应该出现 src 目录,Go 和 Java 不同,在 Go 中每一个目录都是一个包,每一个包都是一等公民,我们不需要将项目代码放到 src 当中,不要用写其他语言的方式来写 Go
# utils,common
不要在项目中出现 utils 和 common 这种包,如果出现这种包,因为我们并不能从包中知道你这个包的作用,长久之后这个包就会变成一个大杂烩,所有东西都往这里面扔。
有的同学这个时候会问说,那我们的工具函数应该放到哪里?怎么放?
举个例子,我们当前使用 gin
作为路由框架,但是 gin
的 handler 注册其实不是很方便,所以我们做了一层封装,这个时候这个工具方法我们一般放在 /pkg/ginx
目录下,表示这个是对 gin
增强的包,不直接使用 gin
作为包名的原因是因为我们在项目中也会引用 gin
相同的命名一个是会导致误解,另一个是在同时导入的时候也会需要去进行重命名会比较麻烦
# 参考资料
Go工程化(二) 项目目录结构 - Mohuishou: https://lailin.xyz/post/go-training-week4-project-layout.html
golang 编程规范 - 项目目录结构
https://makeoptim.com/golang/standards/project-layout