1. 目录规范

一个好的目录结构至少要满足以下几个要求。

  • 命名清晰:目录命名要清晰、简洁,不要太长,也不要太短,目录名要能清晰地表达出该目录实现的功能,并且目录名最好用单数。一方面是因为单数足以说明这个目录的功能,另一方面可以统一规范,避免单复混用的情况。
  • 功能明确:一个目录所要实现的功能应该是明确的、并且在整个项目目录中具有很高的辨识度。也就是说,当需要新增一个功能时,我们能够非常清楚地知道把这个功能放在哪个目录下。
  • 全面性:目录结构应该尽可能全面地包含研发过程中需要的功能,例如文档、脚本、源码管理、API 实现、工具、第三方包、测试、编译产物等。
  • 可预测性:项目规模一定是从小到大的,所以一个好的目录结构应该能够在项目变大时,仍然保持之前的目录结构。
  • 可扩展性:每个目录下存放了同类的功能,在项目变大时,这些目录应该可以存放更多同类功能。

根据功能,我们可以将目录结构分为结构化目录结构和平铺式目录结构两种。

  • 结构化目录结构主要用在 Go 应用中,相对来说比较复杂;
  • 而平铺式目录结构主要用在 Go 包中,相对来说比较简单;

2. 平铺式目录结构

一个 Go 项目可以是一个应用,也可以是一个代码框架 / 库,当项目是代码框架 / 库时,比较适合采用平铺式目录结构。

平铺方式就是在项目的根目录下存放项目的代码,整个目录结构看起来更像是一层的,例如 loggithub.com/golang/glog 就是平铺式的,目录如下:

$ ls glog/
glog_file.go  glog.go  glog_test.go  LICENSE  README

3. 结构化目录结构

当前 Go 社区比较推荐的结构化目录结构是 https://github.com/golang-standards/project-layout。虽然它并不是官方和社区的规范,但因为组织方式比较合理,被很多 Go 开发人员接受。

├── api
├── assets
├── build
├── cmd
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── go.mod
├── init
├── internal
├── LICENSE.md
├── Makefile
├── pkg
├── README_zh-CN.md
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
└── website

一个 Go 项目包含 3 大部分:Go 应用 、项目管理和文档。所以,我们的项目目录也可以分为这 3 大类。同时,Go 应用又贯穿开发阶段、测试阶段和部署阶段,相应的应用类的目录,又可以按开发流程分为更小的子类。所以整体来看,我们的目录结构可以按下图所示的方式来分类:

3.1 Go 应用开发目录

开发的代码包含前端代码和后端代码,可以分别存放在前端目录和后端目录中。

3.1.1 /web

前端代码存放目录,主要用来存放 Web 静态资源,服务端模板和单页应用(SPAs)。

3.1.2 /cmd

一个项目有很多组件,可以把组件 main 函数所在的文件夹统一放在 /cmd 目录下,例如:

$ ls cmd/
gendocs  geniamdocs  genman   genyaml  apiserver iamctl  iam-pump$ ls cmd/apiserver/
apiserver.go

这里要保证 /cmd/<组件名> 目录下不要存放太多的代码,如果你认为代码可以导入并在其他项目中使用,那么它应该位于 /pkg 目录中。如果代码不是可重用的,或者你不希望其他人重用它,请将该代码放到 /internal 目录中。

3.1.3 /internal

存放私有应用和库代码。如果一些代码,你不希望在其他应用和库中被导入,可以将这部分代码放在/internal 目录下。

在引入其它项目 internal 下的包时,Go 语言会在编译时报错:

An import of a path containing the element “internal” is disallowed
if the importing code is outside the tree rooted at the parent of the
"internal" directory.

如果 internal 目录下直接存放每个组件的源码目录(一个项目可以由一个或多个组件组成),当项目变大、组件增多时,可以将新增加的组件代码存放到 internal 目录,这时 internal 目录就是可扩展的。例如:

$ ls internal/
apiserver  authzserver  iamctl  pkg  pump  watcher

/internal 目录建议包含如下目录:

  • /internal/apiserver:该目录中存放真实的应用代码。这些应用的共享代码存放在 /internal/pkg 目录下。
  • /internal/pkg:存放项目内可共享,项目外不共享的包。这些包提供了比较基础、通用的功能,例如工具、错误码、用户验证等功能。

建议是,一开始将所有的共享代码存放在 /internal/pkg 目录下,当该共享代码做好了对外开发的准备后,再转存到 /pkg 目录下。

3.1.4 /pkg

该目录中存放可以被外部应用使用的代码库,其他项目可以直接通过 import 导入这里的代码。所以,我们在将代码库放入该目录时一定要慎重。

3.1.5 /vendor

项目依赖,可通过 go mod vendor 创建。需要注意的是,如果是一个 Go 库,不要提交 vendor 依赖包。

3.1.6 /third_party

外部帮助工具,分支代码或其他第三方应用(例如 Swagger UI)。比如我们 fork 了一个第三方 go 包,并做了一些小的改动,我们可以放在目录 /third_party/forked 下。一方面可以很清楚的知道该包是 fork 第三方的,另一方面又能够方便地和 upstream 同步。

3.2 Go 应用测试目录

3.2.1 /test

用于存放其他外部测试应用和测试数据。/test 目录的构建方式比较灵活:对于大的项目,有一个数据子目录是有意义的。例如,如果需要 Go 忽略该目录中的内容,可以使用 /test/data/test/testdata 目录。

需要注意的是,Go 也会忽略以 ._ 开头的目录或文件。这样在命名测试数据目录方面,可以具有更大的灵活性。

3.3 Go 应用部署目录

3.3.1 /configs

这个目录用来配置文件模板或默认配置。例如,可以在这里存放 confdconsul-template 模板文件。这里有一点要注意,配置中不能携带敏感信息,这些敏感信息,我们可以用占位符来替代,例如:

apiVersion: v1
user:    username: ${CONFIG_USER_USERNAME} # iam 用户名    password: ${CONFIG_USER_PASSWORD} # iam 密码

3.3.2 /deployments

用来存放 IaasPaaS 系统和容器编排部署配置和模板(Docker-Compose,Kubernetes/Helm,Mesos,Terraform,Bosh)。在一些项目,特别是用 Kubernetes 部署的项目中,这个目录可能命名为 deploy

3.3.3 /init

存放初始化系统(systemdupstartsysv)和进程管理配置文件(runitsupervisord)。比如 sysemdunit 文件。这类文件,在非容器化部署的项目中会用到。

3.4 Go 应用项目管理目录

3.4.1 /Makefile

一个 Go 项目在其根目录下应该有一个 Makefile 工具,用来对项目进行管理,Makefile 通常用来执行静态代码检查、单元测试、编译等功能。其他常见功能:

  • 静态代码检查(lint):推荐用 golangci-lint
  • 单元测试(test):运行 go test ./...
  • 编译(build):编译源码,支持不同的平台,不同的 CPU 架构。
  • 镜像打包和发布(image/image.push):现在的系统比较推荐用 Docker/Kubernetes 进行部署,所以一般也要有镜像构建功能。
  • 清理(clean):清理临时文件或者编译后的产物。
  • 代码生成(gen):比如要编译生成 protobuf pb.go 文件。
  • 部署(deploy,可选):一键部署功能,方便测试。
  • 发布(release):发布功能,比如:发布到 Docker Hubgithub 等。
  • 帮助(help):告诉 Makefile 有哪些功能,如何执行这些功能。
  • 版权声明(add-copyright):如果是开源项目,可能需要在每个文件中添加版权头,这可以通过 Makefile 来添加。
  • API 文档(swagger):如果使用 swagger 来生成 API 文档,这可以通过 Makefile 来生成。

建议:直接执行 make 时,执行如下各项 format -> lint -> test -> build,如果是有代码生成的操作,还可能需要首先生成代码 gen -> format -> lint -> test -> build

3.4.2 /scripts

该目录主要用来存放脚本文件,实现构建、安装、分析等不同功能。不同项目,里面可能存放不同的文件,但通常可以考虑包含以下 3 个目录:

  • /scripts/make-rules:用来存放 makefile 文件,实现 /Makefile 文件中的各个功能。Makefile 有很多功能,为了保持它的简洁,我建议你将各个功能的具体实现放在 /scripts/make-rules 文件夹下
  • /scripts/libshell 库,用来存放 shell 脚本。一个大型项目中有很多自动化任务,比如发布、更新文档、生成代码等,所以要写很多 shell 脚本,这些 shell 脚本会有一些通用功能,可以抽象成库,存放在 /scripts/lib 目录下,比如 logging.shutil.sh 等。
  • /scripts/install:如果项目支持自动化部署,可以将自动化部署脚本放在此目录下。如果部署脚本简单,也可以直接放在 /scripts 目录下。

另外,shell 脚本中的函数名,建议采用语义化的命名方式,例如 iam::log::info 这种语义化的命名方式,可以使调用者轻松的辨别出函数的功能类别,便于函数的管理和引用。

3.4.3 /build

这里存放安装包和持续集成相关的文件。这个目录下有 3 个大概率会使用到的目录,在设计目录结构时可以考虑进去。

  • /build/package:存放容器(Docker)、系统(deb, rpm, pkg)的包配置和脚本。
  • /build/ci:存放 CI的配置文件和脚本。
  • /build/docker:存放子项目各个组件的 Dockerfile 文件。

3.4.4 /tools

存放这个项目的支持工具。这些工具可导入来自 /pkg/internal 目录的代码。

3.4.5 /githooks

Git 钩子。比如,我们可以将 commit-msg 存放在该目录。

3.4.6 /assets

项目使用的其他资源 (图片、CSSJavaScript 等)。

3.4.7 /website

如果你不使用 Github 页面,则在这里放置项目的网站数据。

3.5 Go 应用文档目录

3.5.1 /README.md

项目的 README 文件一般包含了项目的介绍、功能、快速安装和使用指引、详细的文档链接以及开发指引等。

3.5.2 /docs

存放设计文档、开发文档和用户文档等(除了 godoc 生成的文档)。推荐存放以下几个子目录:

  • /docs/devel/{en-US,zh-CN}:存放开发文档、hack 文档等。
  • /docs/guide/{en-US,zh-CN}: 存放用户手册,安装、quickstart、产品文档等,分为中文文档和英文文档。
  • /docs/images:存放图片文件。

3.5.3 /CONTRIBUTING.md

开源就绪的项目,用来说明如何贡献代码,如何开源协同等等。CONTRIBUTING.md 不仅能够规范协同流程,还能降低第三方开发者贡献代码的难度。

3.5.4 /api

/api 目录中存放的是当前项目对外提供的各种不同类型的 API 接口定义文件,其中可能包含类似 /api/protobuf-spec/api/thrift-spec/api/http-specopenapiswagger 的目录,这些目录包含了当前项目对外提供和依赖的所有 API 文件。

3.5.5 /LICENSE

版权文件可以是私有的,也可以是开源的。常用的开源协议有:Apache 2.0MITBSDGPLMozillaLGPL

3.5.6 /CHANGELOG

当项目有更新时,为了方便了解当前版本的更新内容或者历史更新内容,需要将更新记录存放到 CHANGELOG 目录。

3.5.7 /examples

存放应用程序或者公共包的示例代码

4. 不建议的目录

4.1 /src

其中一个重要的原因是:在默认情况下,Go 语言的项目都会被放置到$GOPATH/src 目录下。这个目录中存放着所有代码,如果我们在自己的项目中使用/src 目录,这个包的导入路径中就会出现两个 src,例如:

$GOPATH/src/github.com/marmotedu/project/src/main.go

这样的目录结构看起来非常怪。

4.2 /xxs

Go 项目中,要避免使用带复数的目录或者包。建议统一使用单数。

5. 建议

对于小型项目,可以考虑先包含 cmdpkginternal 3 个目录,其他目录后面按需创建,例如:

$ tree --noreport -L 2 tms
tms
├── cmd
├── internal
├── pkg
└── README.md

另外,在设计目录结构时,一些空目录无法提交到 Git 仓库中,但我们又想将这个空目录上传到 Git 仓库中,以保留目录结构。这时候,可以在空目录下加一个 .keep 文件,例如:

$ ls -A build/ci/
.keep

Go 学习笔记(84)— Go 项目目录结构相关推荐

  1. 【skynet学习笔记】skynet的目录结构

    [skynet学习笔记]skynet的目录结构 skynet目录结构 学习skynet必须先了解一下skynet的目录结构. skynet的目录结构十分清晰,一目了然. 下面来具体看看各个目录. [r ...

  2. Linux学习笔记(一)|目录结构

    Linux学习笔记(一) #一.Linux的目录结构 Linux采用层级式的树状目录结构,根目录为"/",在此目录下创建其他目录. /lib 系统开机所需要最基本的动态链接共享库 ...

  3. Linux学习笔记 第三课 目录结构及简单命令

    一.目录结构 二.简单命令(ls,cd,pwd,which,alias,pstree,tree) 三.环境变量PATH应用 四.快捷键 五.扩展知识   (stty  ,ping 及 /var/log ...

  4. 【DM8168学习笔记5】EZSDK目录结构

    EZSDK5.02的目录结构与之前的版本不同,之前的版本各个组件都放在/ezsdk目录下,5.02做了整合. 之前版本:(图片摘自:3.DM816x_1-day_Workshop-Getting_St ...

  5. 《繁凡的深度学习笔记》前言、目录大纲 一文让你完全弄懂深度学习所有基础(DL笔记整理系列)

    <繁凡的深度学习笔记>前言.目录大纲 (DL笔记整理系列) 一文弄懂深度学习所有基础 ! 3043331995@qq.com https://fanfansann.blog.csdn.ne ...

  6. Scrapy:学习笔记(2)——Scrapy项目

    Scrapy:学习笔记(2)--Scrapy项目 1.创建项目 创建一个Scrapy项目,并将其命名为"demo" scrapy startproject demo cd demo ...

  7. Windows进程与线程学习笔记(一)—— 进程结构体

    Windows进程与线程学习笔记(一)-- 进程结构体 进程结构体 EPROCESS +0x000 Pcb : _KPROCESS +0x1b0 Peb : Ptr32 _PEB 练习 解题步骤 第一 ...

  8. 【计算机网络学习笔记】(汇总目录)

    计算机网络学习笔记(汇总目录) 文章目录 点击以下标题,跳转到对应章节的详细讲解 [计算机网络学习笔记01]计算机网络概述(上) [计算机网络学习笔记02]计算机网络概述(中) [计算机网络学习笔记0 ...

  9. JavaWeb黑马旅游网-学习笔记10【项目代码】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  10. STM32学习笔记——MDK新建项目

    STM32学习笔记 KIEL新建项目过程 文章目录 STM32学习笔记 前言 一.keil新建库函数版本项目 二.注意事项 1.文件分类 2.MDK配置 总结 前言 此处使用寄存器版,进行学习.后期看 ...

最新文章

  1. 深入浅出ShellExecute
  2. .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)...
  3. php广告任务网源码_THINKPHP仿我爱广告任务网|任务网站源码下载基于PHP+MYSQL的在线广告打码任务网站源码...
  4. python网络爬虫与信息提取 学习笔记day3
  5. 使用多线程拷贝文件夹
  6. 组装式AI落地新模式,降低企业AI试错成本
  7. [转]Winform不规则窗体的实现心得
  8. 「1s」即可!用 VS Code 一键玩转 GitHub 代码!
  9. 多线程——java练习题
  10. 英特尔nuc能代替主机吗_拆了拆了!Intel NUC装机!小机箱退烧器啊!主机显示器合体...
  11. android下最强的3款pdf阅读器测评
  12. Java架构师技术进阶路线图详解
  13. Structs个人适用知识整理
  14. 2021-09-30 node上传文件的问题以及如何选择适合自己的插件
  15. C语言键盘控制走迷宫小游戏
  16. backtrader的回测结果之Bokel quantstats backtrader_plotting btplotting
  17. 百度地图清除指定覆盖物(Overlay),保留某种覆盖物( disableMassClea),清除保留的覆盖物(enableMassClear)
  18. “一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。本题就请你编写程序帮助老师自动完成这个分配工作,即在得到全班学生的排名后,在当前尚未分组的学生中,
  19. web字体格式及几种在线格式转换工具介绍
  20. 计算机教学改革模式,计算机专业课程教学改革研究

热门文章

  1. 【解决方法】你已从聊天服务器断开,正在尝试重新连接
  2. 2022-2028年中国镀金属膜行业市场研究及前瞻分析报告
  3. 难忘的一天——装操作系统(四)
  4. SpringCloud Alibaba微服务实战(四) - Nacos Config 配置中心
  5. python2转python3文件
  6. 深度学习原理与框架-CNN在文本分类的应用 1.tf.nn.embedding_lookup(根据索引数据从数据中取出数据) 2.saver.restore(加载sess参数)...
  7. 如何将自定义代码生成TVM
  8. PyTorch数据加载处理
  9. 简化可视SLAM应用程序的开发
  10. python %f 怎样指定小数点的位数