17370845950

Golang Web项目常见目录结构如何设计_Golang项目结构规范说明
main.go 应放在 cmd/ 目录下按服务拆分,如 cmd/api/main.go;internal/ 用于模块内复用且对外隔离的代码;config/ 存项目级配置逻辑,pkg/ 存可跨项目复用的组件;HTTP 处理层推荐用 http/ 或 api/,避免 routes/、web/ 等模糊命名。

main.go 放哪?别塞进根目录

Go 项目启动入口 main.go 必须放在可执行包(package main)里,但不建议直接丢在项目根目录。根目录塞太多东西会干扰 go mod init 和 IDE 识别——比如 go list ./... 可能意外包含测试工具或配置脚本。

推荐做法是建一个 cmd/ 目录,按服务拆分子命令:

  • cmd/api/main.go —— HTTP 服务主入口
  • cmd/migrate/main.go —— 数据库迁移工具
  • cmd/admin/main.go —— 后台管理 CLI

这样 go build -o bin/api cmd/api 路径清晰,也方便 CI 分别构建不同二进制。

internal/ 不是摆设,它管的是“不能被外部 import”的代码

internal/ 是 Go 官方约定的私有包路径,任何在 internal/ 下的包,只有其父目录或同级目录的模块才能导入。比如 github.com/user/project/internal/handler 只能被 github.com/user/project/cmd/apigithub.com/user/project/internal/service 导入,第三方无法 import 它。

常见误用:

  • 把通用工具函数(如 utils.StringToTime)放 internal/utils → 应该挪到 pkg/utils 或直接开独立小模块
  • internal/

    里写数据库模型(model.User),结果 cmd/apicmd/migrate 都要重复定义 → 模型应统一放在 internal/model,由各 cmd 显式引用

记住:internal/ 的边界是「模块内复用 + 对外隔离」,不是「随便藏代码的地方」。

config/ 和 pkg/ 怎么分?看是否跨项目复用

config/ 存当前项目的加载逻辑:解析 config.yaml、绑定环境变量、校验必填字段。它的结构通常紧贴运行时需求,比如:

config/
├── config.go          // Load() + Validate()
├── config_test.go
└── sample.yaml

pkg/ 则是真正可剥离的、带明确接口契约的组件。例如:

  • pkg/logger —— 封装 zerolog,暴露 Logger 接口和 NewLogger()
  • pkg/db —— 封装 sqlx 初始化、连接池配置,不暴露具体 driver
  • pkg/httpclient —— 带默认超时、重试、metrics 上报的 client 工厂

如果某段代码只在这个项目里用,且不打算抽成独立 GitHub repo,就别硬塞 pkg/;反过来,如果已经写了 pkg/cache 但发现其他项目也要用,那就该单独 go mod init github.com/user/cache 拆出去。

handlers/ 和 api/ 目录名之争:优先选语义明确的

HTTP 路由处理层命名混乱很常见:handlers/controllers/api/http/……实际没标准,但要注意两点:

  • 名字得让新成员一眼看出「这里只做请求接收、参数解析、响应包装,不写业务逻辑」
  • 避免和已有 Go 生态术语冲突,比如 controllers/ 容易让人联想到 Gin 的 gin.HandlerFunc,但如果你用的是 net/http 原生路由,反而造成认知偏差

更稳妥的选择是:

  • http/ —— 表明技术栈,适合强调协议层职责(如中间件、路由注册、错误转 HTTP 状态码)
  • api/ —— 表明领域,适合已划分清楚「API 层 vs Service 层」的团队,且 API 版本管理(v1/v2)会自然落到这个目录下

别用 routes/ —— 它只是个注册动作,不是职责主体;也别用 web/ —— 太宽泛,CLI 或 gRPC 服务也可能跑在 web server 上。