Go 项目目录结构清单,照这个摆就行
混乱的 Go 项目都有一个共同点:改代码的时候得靠全局搜索找文件,而不是靠目录名猜位置。这份 Go 项目目录结构清单来自我自己维护的服务,六条,每条对应一个具体的摆放决定。
它不是标准答案。一个人维护的项目和十个人的团队,目录诉求不一样,我这份是给前者的。下面这六条,就是我那套 Go 项目目录结构怎么设计的答案,每条对应一个具体的摆放决定。
一、先定下 Go 项目目录结构怎么设计的两条边界
根目录最容易变成杂物间。我的规则是:根目录只放进程入口、构建脚本、依赖声明和说明文档。
业务代码一律进子目录,这条定下来之后,翻项目的时候第一眼就能看清这是个什么程序。
还有一条边界是:Go 有明确的机制限制「哪些包能被外部引用」,用它来划内外很省事。只给外部调用的部分留在顶层,其余全部放进受保护的目录。
具体动作:先把根目录里的业务文件挪进内层目录,只留一个入口文件。
二、可执行文件按名字分目录
如果一个仓库要产出多个可执行文件,每个占一个目录,入口文件放在里面。
好处是构建命令能一条条写清楚,不用记住哪个文件对应哪个产物。我踩过的坑是把两个入口文件平铺在根目录,后来加第三个的时候彻底乱了。
具体动作:给每个可执行文件建一个同名目录,入口文件固定叫入口名。
三、启动组装和业务逻辑分开
这一步决定了你的目录结构能撑多久。
把「读配置、连数据库、注册路由」这类启动组装放在一处,业务逻辑放在另一处。这样测试业务逻辑时不用起完整进程,排查启动问题时也不用翻业务代码。
判断标准很简单:业务包里不应该出现读配置的代码。具体动作:找出所有读配置的调用,把它们收拢到启动那一层。
五、对外契约的定义不要和实现混在一起
接口定义和实现放在一起,是我早期最常见的做法,后来发现对接方会因此依赖一堆用不到的东西。
现在的做法是把对外契约抽到一个独立目录,实现放在内部。
具体动作:把请求和响应的结构定义从各个处理函数里提出来,集中到一处。
六、放不下的时候先想命名再想加层
目录越加越多是必然趋势,但加层之前先看命名。
我遇到过一层叫「工具」的目录,里面塞了十几类东西。问题不是层次不够,是名字太宽。改成分类型的名字之后,那层自然就散了。
具体动作:翻一遍自己的目录,找出名字里带「通用」「工具」「杂项」的那些,按实际用途改名或者拆分。
自己核对一遍
| 检查项 | 不通过的表现 |
|---|---|
| 根目录内容 | 出现业务代码文件 |
| 对外暴露范围 | 外部能引用到内部包 |
| 入口文件位置 | 多个入口平铺在同一层 |
| 配置读取位置 | 业务包里出现读配置 |
| 契约定义位置 | 结构和实现混在一起 |
| 目录命名 | 出现「通用」「工具」这类宽名字 |
六条里如果只能做一条,我会选第三条。启动和业务分开,是后面所有调整的前提,其他几条都可以晚一点再改。
常见问题
Q:Go 项目目录结构怎么设计,小项目也要这么细吗?
A:不用,按 Go 项目目录结构怎么设计这个问题来看,只有一个入口、没有外部引用的小项目,前两条可以省掉。第三条建议一开始就做,因为它改起来最贵。
Q:一定要用社区里推荐的那套标准布局吗?
A:不必。标准布局是为「会被别人引用的库」准备的,自己的服务照搬会多出好几层空目录,反而不好找东西。
Q:目录结构多久调一次合适?
A:别定周期,定触发条件。出现「同一个目录里放了两种用途的东西」时就调,比按时间调有效。
相关阅读
本文作者西米,37 岁程序员,一个人加五个 AI 员工在跑一家公司。踩过的坑和真实账本都写在公众号「西米分享」。
完整代码、踩坑清单与最新进展,都在公众号。
📱 关注公众号「西米分享」