Standard Go Project Layout
https://github.com/golang-standards/project-layout/blob/master/README_zh.md
如果你尝试学习 Go,或者你正在为自己建立一个 PoC 或一个玩具项目,这个项目布局是没啥必要的。从一些非常简单的事情开始(一个 main.go 文件绰绰有余)。当有更多的人参与这个项目时,你将需要更多的结构,包括需要一个 toolkit 来方便生成项目的模板,尽可能大家统一的工程目录布局。
/cmd
本项目的主干。
每个应用程序的目录名应该与你想要的可执行文件的名称相匹配(例如,/cmd/myapp)。即一个工程就在/cmd中有一个相应的 main.go 文件,执行 go build 之后会生成一个 cmd 的二进制文件,为了使这个二进制文件名称是我们想要的名称,建议在/cmd下面再见一个目录变为 /cmd/myapp,这样生成的二进制可执行文件名称就是 myapp 名称了,跟项目名称对齐。
不要在这个目录中放置太多代码。
如果你认为代码可以导入并在其他项目中使用(对外使用),那么它应该位于 /pkg
目录中。
如果代码不是可重用的,或者你不希望其他人重用它,请将该代码放到 /internal
目录中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e29qCYwj-1621761130968)(E:\MyFile\GO语言进阶训练营\第04周工程化实践\cmd目录视图.png)]
/internal
对内共享
私有应用程序和库代码。这是你不希望其他人在其应用程序或库中导入代码。请注意,这个布局模式是由 Go 编译器本身执行的。有关更多细节,请参阅Go 1.4 release notes。注意,你并不局限于顶级 internal 目录,在项目树的任何级别上都可以有多个内部目录(内部的 internal 目录)。
你可以选择向 internal 包中添加一些额外的结构,以分隔共享和非共享的内部代码。这不是必需的(特别是对于较小的项目),但是最好有有可视化的线索来显示预期的包的用途。你的实际应用程序代码可以放在 /internal/app
目录下(例如 /internal/app/myapp
),这些应用程序共享的代码可以放在 /internal/pkg
目录下(例如 /internal/pkg/myprivlib
)。
因为我们习惯把相关的服务,比如账号服务,内部有 rpc、job、admin 等,相关的服务整合一起后,需要区分 app。单一的服务,可以去掉 /internal/myapp
。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRXMOTKM-1621761130970)(E:\MyFile\GO语言进阶训练营\第04周工程化实践\internal目录视图.png)]
/pkg
对外共享
外部应用程序可以使用的库代码(例如 /pkg/mypubliclib)。其他项目会导入这些库,所以在这里放东西之前要三思:-)注意,internal 目录是确保私有包不可导入的更好方法,因为它是由 Go 强制执行的。/pkg 目录仍然是一种很好的方式,可以显式地表示该目录中的代码对于其他人来说是安全使用的好方法。
/pkg 目录内,可以参考 go 标准库的组织方式,按照功能分类。/internla/pkg 一般用于项目内的 跨多个应用的公共共享代码,但其作用域仅在单个项目工程内。
由 Travis Jeffery 撰写的 I'll take pkg over internal
博客文章提供了 pkg 和 internal 目录的一个很好的概述,以及什么时候使用它们是有意义的。
当根目录包含大量非 Go 组件和目录时,这也是一种将 Go 代码分组到一个位置的方法,这使得运行各种 Go 工具变得更加容易组织。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8iZ0Nz5-1621761130971)(E:\MyFile\GO语言进阶训练营\第04周工程化实践\pkg目录视图.png)]
kit Project Layout
每个公司都应当为不同的微服务建立一个统一的 kit 工具包项目(基础库/框架) 和 app 项目(具体应用)。
基础库 kit 为独立项目,公司级建议只有一个,按照功能目录来拆分会带来不少的管理工作,因此建议合并整合。
by Package Oriented Design:
“To this end, the Kit project is not allowed to have a vendor folder. If any of packages are dependent on 3rd party packages, they must always build against the latest version of those dependences.”
kit 项目必须具备的特点:
- 统一
- 标准库方式布局
- 高度抽象–扩展性强
- 支持插件
-
comtainer:容器
-
pool:连接池
-
queue:队列
-
-
ecode:错误码
Service Application Project Layout
服务应用的项目结构
/api
API 协议定义目录,xxapi.proto protobuf 文件,以及生成的 go 文件(包含服务是什么、输入输出是什么)。我们通常把 api 文档直接在 proto 文件中描述(定义即文档,文档即定义)。
/configs
具体的配置文件模板或默认配置。
/test
额外的外部测试应用程序和测试数据。你可以随时根据需求构造 /test 目录。对于较大的项目,有一个数据子目录是有意义的。例如,你可以使用 /test/data 或 /test/testdata (如果你需要忽略目录中的内容)。请注意,Go 还会忽略以“.”或“_”开头的目录或文件,因此在如何命名测试数据目录方面有更大的灵活性。
不应该包含:/src
有些 Go 项目确实有一个 src 文件夹,但这通常发生在开发人员有 Java 背景,在那里它是一种常见的模式。不要将项目级别 src 目录与 Go 用于其工作空间的 src 目录。
internal
一个项目是通过API的方式对外暴露接口,internal 实际上是内部的业务逻辑层、数据操作层、服务层等内部的代码。
一个 gitlab 的 project 里可以放置多个微服务的app(类似 monorepo)。也可以按照 gitlab 的 group 里建立多个 project,每个 project 对应一个 app。
- 多 app 的方式,app 目录内的每个微服务按照自己的全局唯一名称,比如 “account.service.vip” 来建立目录,如: app/account/vip/*。
- 和 app 平级的目录 pkg 存放业务有关的公共库(非基础框架库)。如果应用不希望导出这些目录,可以放置到 myapp/internal/pkg 中。
微服务中的 app 服务类型分为5类:interface、service、job、admin、task。
- interface: 对外的 BFF 服务,接受来自用户的请求,比如暴露了 HTTP/gRPC 接口。
- service: 对内的微服务,仅接受来自内部其他服务或者网关的请求,比如暴露了gRPC 接口只对内服务。
- admin:区别于 service,更多是面向运营测的服务,通常数据权限更高,隔离带来更好的代码级别安全。
- job: 流式任务处理的服务,上游一般依赖 message broker。持续的消费上游的一些数据,然后做一些业务逻辑处理,其偏向一个异步的一个流程。
- task: 定时任务,类似 cronjob,部署到 task 托管平台中。
cmd 应用目录负责程序的: 启动、关闭、配置初始化等。
app目录模型:
app 目录下有 api、cmd、configs、internal 目录,目录里一般还会放置 README、CHANGELOG、OWNERS。
-
internal: 是为了避免有同业务下有人跨目录引用了内部的 biz、data、service 等内部 struct。
-
biz: 业务逻辑的组装层,类似 DDD 的 domain 层,data 类似 DDD 的 repo,repo 接口在这里定义,使用依赖倒置的原则。
-
data: 业务数据访问,包含 cache、db 等封装,实现了 biz 的 repo 接口。我们可能会把 data 与 dao 混淆在一起,data 偏重业务的含义,它所要做的是将领域对象重新拿出来,我们去掉了 DDD 的 infra 层。
-
service: 实现了 api 定义的服务层(对外暴露rpc的),类似 DDD 的 application 层,处理 DTO 到 biz 领域实体的转换(DTO -> DO),同时协同各类 biz 交互,但是不应处理复杂逻辑。
PO(Persistent Object): 持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么数据表中的每个字段(或若干个)就对应 PO 的一个(或若干个)属性。https://github.com/facebook/ent