types 目录,到底干啥的?每个 go-zero 项目里都有 internal/types/types.go。它装的是什么?为什么偏偏叫 types?是手写的还是生成的?别的框架也这么干吗?
一句话:它是 API 层的数据形状(DTO) 集中存放处 —— 所有「请求长什么样、响应长什么样」的结构体。
go-zero 把 .api 文件里你用 type 声明的所有结构体,原样生成到 internal/types/types.go。这里面没有逻辑、没有方法,纯粹是「数据的样子」:请求体、响应体、以及被多个接口复用的公共结构。
// 由 user.api 自动生成,不要手改大块逻辑 type LoginReq struct { Username string `json:"username"` Password string `json:"password"` } type LoginResp struct { Token string `json:"token"` ExpireAt int64 `json:"expireAt"` } // 被多个接口复用的公共结构也放这里 type PageReq struct { Page int64 `json:"page"` Size int64 `json:"size"` }
model 层)、业务逻辑、配置项、中间件。types 只描述「数据的形状」,不描述「怎么算」。这不是随便起的文件夹名 —— 它直接对应 .api 文件里的 type 关键字。
go-zero 的 API 描述语言里,定义一个数据结构用的就是 type 关键字:
type LoginReq { ... }
goctl 扫描所有 type 声明,把它们翻译成 Go 的 struct,统一吐进一个叫 types 的包。所以文件夹名 = 「这些 type 定义」的归宿,名字和 DSL 关键词一一对应,看着就懂。
# user.api type LoginReq { Username string `json:"username"` Password string `json:"password"` } // ↓ goctl 生成 ↓ // internal/types/types.go type LoginReq struct { Username string `json:"username"` Password string `json:"password"` }
dto / schema / model / vo,go-zero 因为是从 type 关键字生成,就直白地叫了 types。关键点:types 不是你手敲的,是 goctl api go 一次性生成的。你只改 .api 源文件。
goctl api go -api user.api -dir . # 扫描 .api,产出 handler/logic/svc/types
types.go 里加大量手写字段后再跑 goctl —— 重新生成会覆盖。正确姿势是:所有数据形状都在 .api 里用 type 定义,让 goctl 重新生成 types.go。需要给 struct 加方法时,才在 types 包里另写文件(不会被覆盖)。「给 API 数据形状一个专门归宿」是全圈共识,但 命名和是否自动生成 差别很大。
| 框架 / 语言 | 对应目录 / 文件 | 怎么来的 | 和 go-zero 的 types 像吗 |
|---|---|---|---|
| go-zero (Go) | internal/types/types.go |
goctl 从 .api 生成 |
命名最直白,强约束 + 代码生成 |
| gRPC / protobuf (Go) | xxx.pb.go 里的 message |
protoc 从 .proto 生成 |
★ 最像:都是「写 schema → 生成类型」。仅 RPC 场景 |
| Spring Boot (Java) | dto/ · vo/ · request/ · response/ |
手写类 + Jackson 注解 | 概念一致,但纯手写、命名更碎、无生成 |
| FastAPI (Python) | schemas.py 里的 Pydantic Model |
手写,类型提示驱动校验 | 很像「数据契约」理念,但放在一个 schemas 文件而非 types 目录 |
| Gin / Echo (Go) | 就近定义 struct,或 pkg/dto |
手写,json/form tag 绑定 |
没有自动生成的固定 types 目录,放哪都行 |
| net/http (Go 原生) | 你自己定,没有约定 | 手写 | 完全自由,也完全没规范 |
有没有「专门放 API 数据形状的地方」?基本都有。但 go-zero 的 types 有几个独特标签。
type 关键字;② 自动生成且和 .api 强绑定;③ 和 handler/logic/svc 一起构成「生成即分层」的硬规范。多数其他 Go 框架没有这层代码生成约束。改字段只动 .api,然后 goctl api go 重新生成 types.go。别手改生成的文件。
像 PageReq / IdReq 这种被多个接口共用的,定义一次,多处引用,types 包天然适合承载。
若想给某个 struct 加校验/格式化方法,在 types 目录新建 types_ext.go 之类文件,goctl 不会覆盖你额外加的源码文件。
API 的 LoginReq 和数据库表结构不是一回事。数据库模型在 model 层(go-zero 用 goctl model 从表结构生成),别混进 types。