go项目目录结构是编译器和工具链的行为契约:cmd/下须按可执行名建子目录,internal/是硬编码私有边界,测试文件必须与源码同目录同包。

Go 项目目录结构不是风格偏好,而是编译器和工具链的行为契约——写错位置,go build 会报错,go test 会漏用例,go install 会生成错的二进制名。
cmd/ 下必须按可执行名建子目录
Go 工具链通过 cmd/xxx/main.go 的路径推导最终二进制名:cmd/api/main.go → go build 输出 ./api;若直接放 cmd/main.go,输出就是 ./cmd,与预期不符。
- 多个命令(如
api+worker)必须分目录,否则go build ./cmd会同时编译所有main包,触发multiple main packages错误 -
main.go内只做三件事:解析 flag、加载config、调用app.Run();业务逻辑必须移出,哪怕只有一行也不该留在cmd/下 - 不要在
cmd/下建cmd/api/handler/这类子包——它不属于入口层,应归入internal/或pkg/
internal/ 是编译器级私有边界,不是代码垃圾桶
internal/ 不是“放内部代码的文件夹”,而是 Go 语言硬编码的导入限制机制:任何位于 github.com/user/project/internal/foo 的包,**仅允许被 github.com/user/project/ 下的包导入**;外部模块引用直接报错 use of internal package not allowed。
- 把真正不该被复用的实现细节放这里,比如
internal/tradebot/(含状态机、私有回调)、internal/dbmigration/ - 避免
internal/util/或internal/common/—— 这类命名放弃语义,很快变成函数堆砌场,且违背“高内聚”原则 - 如果某个包未来可能开源复用(如通用日志封装),就别放
internal/,改放pkg/logger/或顶层路径(如github.com/user/project/storage)
测试文件必须与源码同目录同包,且命名严格匹配
Go 不支持集中式 test/ 目录。go test ./ 只扫描当前模块下所有 *_test.go 文件,并要求它们与同目录的非 _test.go 文件属于同一 package,否则编译失败。
-
internal/tradebot/tradebot.go(package tradebot)的测试必须是internal/tradebot/tradebot_test.go(同样package tradebot) - 想测私有函数?测试文件必须和源文件同包;想黑盒测导出接口?可用
package tradebot_test,但此时无法访问未导出字段或方法 - 不要把
tradebot_test.go放到test/或internal/test/下——go test根本找不到它
最容易被忽略的是 internal/ 的语义约束:它不是“内部代码集合”,而是“不可导出的抽象边界”。一旦把领域逻辑塞进 internal/util/,就等于放弃了职责划分,后续重构时连依赖图都画不出来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











