kratos new 生成的目录结构不能直接修改,因其强耦合官方模板布局:重命名或移动 /cmd、/internal、/api 会导致 proto 生成失败、wire 注入报错、main.go 编译失败及 CI 脚本失效;正确做法是用 kratos new myservice 重新生成。

kratos new 生成的目录结构为什么不能直接改
因为 kratos new 拉取的是官方模板仓库(默认 https://github.com/go-kratos/kratos-layout@main),整个项目骨架、go.mod 依赖版本、Makefile 构建逻辑、甚至 wire 初始化顺序都强耦合在该 layout 中。随意重命名或移动 /cmd、/internal、/api 目录会导致:
-
kratos proto server找不到-t internal/service对应路径,生成失败 -
wire gen报错:无法解析internal/app/xxx包导入链 -
go run ./cmd/xxx编译失败——Go 编译器要求main.go必须在cmd/xxx/下,且包名是main - CI 脚本中硬编码的
DockerfileCOPY 路径(如COPY ./internal ./internal)失效
如果你只是想换服务名,正确做法是:kratos new myservice,而不是生成后手动改名。
internal 和 pkg 目录的边界到底怎么划
关键不是“能不能放”,而是“谁有权 import”。Go 编译器只强制 internal 下的包**不能被本项目外的模块 import**,但不阻止你在本项目内跨 internal 子目录 import——这恰恰是容易踩坑的地方。
-
/internal/app/xxx:只供本服务二进制使用,比如myservice的启动逻辑、HTTP/gRPC server 初始化。其他服务绝不该 import 它 -
/internal/pkg/xxx:项目内多个服务共用的私有工具,比如统一的错误码封装ecode、通用 ID 生成器。可被本项目任意internal/app/*import,但外部仓库禁止引用 -
/pkg/xxx:设计为对外发布的库,比如你封装的 Redis 封装pkg/redisx,别人go get your-repo/pkg/redisx就能用。它必须有完整测试、文档、语义化版本
常见错误:把本该放 /pkg 的通用日志中间件塞进 /internal/pkg,结果另一个团队想复用时发现 import 报错——这时才意识到它本该是公开 API。
proto 文件放在 api/ 下,但生成代码却进 internal/,为什么
这是 Kratos 明确的分层约定:api/ 只存原始 .proto 文件(纯契约),而生成的 Go 代码(*_grpc.pb.go、*_http.pb.go)必须进 internal/ 或 pkg/,原因有三:
- 避免污染
api/目录:.proto 是人类可读的接口定义,生成代码是机器产物,混在一起不利于 review 和 diff - 控制依赖流向:生成代码依赖
google.golang.org/grpc等,若放在api/,可能导致go list -deps错误地把 grpc 当作业务依赖暴露给外部 - 符合 Go 的 visibility 规则:生成代码里的 struct 方法、常量等,通常只应在服务内部使用,放进
internal是最自然的封装
执行 kratos proto server api/metadata/metadata.proto -t internal/service 时,-t 参数就是显式指定目标目录。别手贱改成 -t api,否则后续 wire 注入会找不到 service 接口实现。
wire 依赖注入文件为什么必须叫 wire.go
因为 wire 工具默认只扫描 **/wire.go 文件(可通过 -generate 指定,但 Kratos 模板没配)。更关键的是,wire.go 里写的 InitApp 函数签名和返回值类型,是 cmd/xxx/main.go 中 wire.Build(InitApp) 唯一能识别的入口。
- 如果改名成
di.go,wire gen不会处理它,main.go编译时报错:undefined: InitApp - 如果删掉
InitApp函数,或者返回类型不是*app.App,wire gen会报 “no providers found for *app.App” -
wire.go里不能写业务逻辑,只能写wire.Struct、wire.Bind这类声明式注入语句——这里写log.Info("initing")看似无害,实则破坏了 DI 的纯声明性,调试时难以区分是初始化失败还是日志打错了
真正容易被忽略的点:wire 生成的 wire_gen.go 文件,必须和 wire.go 在同一包下,且不能被 go mod vendor 收录(它本质是生成代码,不是源依赖)。











