Go 项目目录结构清单,照这个摆就行

混乱的 Go 项目都有一个共同点:改代码的时候得靠全局搜索找文件,而不是靠目录名猜位置。这份 Go 项目目录结构清单来自我自己维护的服务,六条,每条对应一个具体的摆放决定。

它不是标准答案。一个人维护的项目和十个人的团队,目录诉求不一样,我这份是给前者的。下面这六条,就是我那套 Go 项目目录结构怎么设计的答案,每条对应一个具体的摆放决定。

一、先定下 Go 项目目录结构怎么设计的两条边界

根目录最容易变成杂物间。我的规则是:根目录只放进程入口、构建脚本、依赖声明和说明文档。

业务代码一律进子目录,这条定下来之后,翻项目的时候第一眼就能看清这是个什么程序。

还有一条边界是:Go 有明确的机制限制「哪些包能被外部引用」,用它来划内外很省事。只给外部调用的部分留在顶层,其余全部放进受保护的目录。

具体动作:先把根目录里的业务文件挪进内层目录,只留一个入口文件。

二、可执行文件按名字分目录

如果一个仓库要产出多个可执行文件,每个占一个目录,入口文件放在里面。

好处是构建命令能一条条写清楚,不用记住哪个文件对应哪个产物。我踩过的坑是把两个入口文件平铺在根目录,后来加第三个的时候彻底乱了。

具体动作:给每个可执行文件建一个同名目录,入口文件固定叫入口名。

三、启动组装和业务逻辑分开

这一步决定了你的目录结构能撑多久。

把「读配置、连数据库、注册路由」这类启动组装放在一处,业务逻辑放在另一处。这样测试业务逻辑时不用起完整进程,排查启动问题时也不用翻业务代码。

判断标准很简单:业务包里不应该出现读配置的代码。具体动作:找出所有读配置的调用,把它们收拢到启动那一层。

五、对外契约的定义不要和实现混在一起

接口定义和实现放在一起,是我早期最常见的做法,后来发现对接方会因此依赖一堆用不到的东西。

现在的做法是把对外契约抽到一个独立目录,实现放在内部。

具体动作:把请求和响应的结构定义从各个处理函数里提出来,集中到一处。

六、放不下的时候先想命名再想加层

目录越加越多是必然趋势,但加层之前先看命名。

我遇到过一层叫「工具」的目录,里面塞了十几类东西。问题不是层次不够,是名字太宽。改成分类型的名字之后,那层自然就散了。

具体动作:翻一遍自己的目录,找出名字里带「通用」「工具」「杂项」的那些,按实际用途改名或者拆分。

自己核对一遍

| 检查项 | 不通过的表现 |

|---|---|

| 根目录内容 | 出现业务代码文件 |

| 对外暴露范围 | 外部能引用到内部包 |

| 入口文件位置 | 多个入口平铺在同一层 |

| 配置读取位置 | 业务包里出现读配置 |

| 契约定义位置 | 结构和实现混在一起 |

| 目录命名 | 出现「通用」「工具」这类宽名字 |

六条里如果只能做一条,我会选第三条。启动和业务分开,是后面所有调整的前提,其他几条都可以晚一点再改。

常见问题

Q:Go 项目目录结构怎么设计,小项目也要这么细吗?

A:不用,按 Go 项目目录结构怎么设计这个问题来看,只有一个入口、没有外部引用的小项目,前两条可以省掉。第三条建议一开始就做,因为它改起来最贵。

Q:一定要用社区里推荐的那套标准布局吗?

A:不必。标准布局是为「会被别人引用的库」准备的,自己的服务照搬会多出好几层空目录,反而不好找东西。

Q:目录结构多久调一次合适?

A:别定周期,定触发条件。出现「同一个目录里放了两种用途的东西」时就调,比按时间调有效。

相关阅读


本文作者西米,37 岁程序员,一个人加五个 AI 员工在跑一家公司。踩过的坑和真实账本都写在公众号「西米分享」。

完整代码、踩坑清单与最新进展,都在公众号。

📱 关注公众号「西米分享」