main.go 绝不能放在项目根目录,否则会因多个 package main 文件被 go build 扫描到而报“multiple main packages”错误,破坏多命令构建、测试隔离与依赖边界;正确做法是将各 main 入口置于 cmd/ 子目录下,严格遵循“一目录一 main 包”原则。

main.go 绝不能放在项目根目录,否则很快会触发 “multiple main packages” 错误,且无法支持多命令构建、测试隔离和依赖边界控制。
为什么 go build 会报 “multiple main packages”
Go 工具链按目录识别包,所有 package main 文件必须严格归属同一目录。一旦你在根目录放了 main.go,又不小心加了 cli.go 或 migrate.go(也都声明 package main),go build ./ 就会扫描到多个 main 包并直接失败。
更隐蔽的问题是:根目录混入非 main 文件(如 router.go)却没声明包名,或错误声明为 package main,也会导致同样报错。
- 根本原因不是“文件太多”,而是“多个目录下存在
package main”或“同一目录下有多个main入口” -
go run .和go test ./都依赖此规则,破坏它会让整个工具链行为不可预测 - 用
go list ./...可快速检查:它会列出所有被识别为独立包的路径,帮你定位意外的main目录
cmd/ 目录下怎么放 main.go 才算合规
cmd/ 是 Go 官方约定的可执行程序入口区,每个子目录对应一个独立二进制,且必须满足“一目录一 main 包”原则。
-
cmd/api/main.go→ 构建出api二进制,只加载 HTTP server 依赖 -
cmd/migrate/main.go→ 构建出migrate二进制,只引入 DB 迁移逻辑,不带 HTTP router - 运行命令统一为:
go run cmd/api/main.go;构建命令为:go build -o bin/api cmd/api - 禁止在
cmd/xxx/下再建子目录放.go文件——这会创建新包,破坏main的唯一性
internal/ 和 pkg/ 到底该放什么
这两个目录划定了 Go 的“可见性边界”:internal/ 下的包只能被本模块导入,pkg/ 是设计为可被外部 go get 复用的公共组件。
-
internal/trade/:存放交易核心逻辑,含领域模型、状态机、业务规则,不依赖net/http或框架类型 -
internal/logger/:封装 zap 或 zerolog 的初始化与字段注入,仅供本项目使用 -
pkg/validator/:提供通用校验器(如ValidateEmail()),有完整单元测试和文档,可独立发布 - 反例:
internal/utils/或internal/common/—— 这类命名往往意味着职责模糊,后期极易变成“啥都往里塞”的黑洞
测试文件为什么必须和源码同目录同包
Go 的 go test 不靠配置发现测试,而是靠文件路径和包声明双重匹配:测试文件必须和被测代码在同一目录、同名包(不含 _test 后缀)、且以 _test.go 结尾。
-
internal/trade/trade.go(package trade)→ 测试文件必须是internal/trade/trade_test.go(也声明package trade) - 想测
internal/trade里的未导出函数?只能在同目录写测试,不能挪到根目录或test/下——Go 没有“全局 test 目录”概念 - 若测试需要模拟依赖(如 DB),应在
trade/包内定义接口(如type OrderRepo interface{}),并在测试中传入 mock 实现
最容易被忽略的是 go.mod 的 module 名:它不是项目代号,而是所有 import 语句的根路径。写成 module myapp 会导致别人 import "myapp/internal/trade" 时失败——正确写法是 module github.com/user/myapp,和仓库地址严格一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











