Go不支持子目录嵌套go.mod,因模块以go.mod为边界且需唯一路径;强行添加会导致构建错误或依赖混乱,正确分层应通过internal/pkg包组织+模块路径语义实现。
go mod init 后不能直接嵌套多层 go.mod
Go 的模块系统(module)以 go.mod 文件为边界,每个模块必须有唯一、可导入的模块路径(如 github.com/user/project),且 go 工具链**不支持子目录下再声明独立模块**——除非你显式运行 go mod init 创建新模块。强行在子目录放 go.mod 会导致:go build 报错 main module does not contain package,或依赖解析混乱。
真正的“分层管理”不是靠物理嵌套模块,而是靠包(package)组织 + 模块路径语义 + 显式依赖声明。
go.mod 是整个项目的主模块,所有内部包都属于它(即使路径是 internal/service 或 pkg/repository)github.com/user/repo-core),必须:独立 Git 仓库 + 独立 go.mod + 语义化版本 + 通过 require 引入internal/ 目录天然限制外部导入,适合放仅本项目使用的分层逻辑(如 internal/handler, internal/usecase, internal/gateway)Go 不强制 MVC 或六边形架构,但你可以通过包名和路径表达职责边界。关键不是“能不能分”,而是“怎么让 import 路径体现意图”。
推荐目录结构示例:
myapp/
├── go.mod
├── main.go
├── cmd/
│ └── myapp/
│ └── main.go // 只含初始化和启动逻辑
├── internal/
│ ├── handler/ // HTTP/gRPC 入口,只依赖 usecase
│ ├── usecase/ // 业务逻辑,只依赖 entity + gateway
│ ├── entity/ // 领域模型(structs + methods),无外部依赖
│ └── gateway/ // 外部依赖适配器(DB、HTTP client),实现 usecase 定义的 interface
└── pkg/
└── util/ // 可被 internal 和 cmd 共享的纯工具函数(无业务逻辑)
internal/ 下各包之间可以相互 import,但外部模块无法 import internal/xxx —— 这是 Go 编译器强制的封装机制pkg/ 是显式开放的公共包,适合提取通用能力(如 pkg/logger, pkg/metrics),可被其他项目 go get
handler 不能 import gateway,只能通过 usecase 定义的 interface 间接使用不要为了“分层”而拆模块。Go 模块的本质是**可独立版本化、可被他人复用的单元**。拆之前问自己:

go get 使用?v1.0.0 并遵守 semver?internal/ 包?(否则就是假独立)常见误拆场景:
internal/repository 单独提成模块 → 它强依赖本项目的 entity,别人无法单独用go mod tidy 会混乱,应统一用 monorepo + 单模块,靠 cmd/ 分离可执行文件go list -f 和 go mod graph 是验证分层的实际工具写完包结构别光靠眼睛看,用命令验证依赖流向是否符合预期:
go list -f '{{.Deps}}' internal/handlerentity 依赖了 gateway):go mod graph | grep 'entity.*gateway'(应无输出)
goplantuml):go install github.com/evanw/esbuild/cmd/esbuild@latest && go install github.com/awalterschulze/goplantuml@latest
真正难的不是建目录,而是让每个 import 语句都传递明确的抽象意图——比如 import "myapp/internal/usecase" 表示“我需要业务能力”,而不是“我需要调 DB”。