必须按数据源、鉴权粒度、SLA要求拆分独立服务;group决定代码目录结构,prefix决定HTTP路径;多.api文件需通过相对路径import公共types避免重复定义。

当你在 go-zero 项目中面对几十个接口、多个业务模块、不同鉴权等级和路由前缀时,必须明确:哪些接口该放进同一个 .api 文件、哪些该拆成独立服务、group 和 prefix 到底谁管目录结构谁管 HTTP 路径——边界模糊会导致生成代码混乱、路由冲突、types 重复、团队协作时改一个接口牵动三个服务。
先看一个典型误操作场景
把用户管理、订单查询、支付回调全写进 user.api,用 @server 块硬塞 group: order 和 group: pay;结果 goctl api go -api user.api -dir . 生成的 handler 全挤在 internal/handler/user/ 下,但路由却注册成了 /order/list 和 /pay/callback,而 types.go 里混着 UserReq、OrderListReq、PayCallbackReq 三个结构体——这已不是分组问题,是服务边界彻底失守。
判断是否该拆成独立服务
满足以下任一条件,就必须拆出新服务,不能靠 group 模拟:
① 接口背后的数据源或存储介质不同:比如用户信息查 MySQL,商品库存查 Redis,订单明细查 TiDB —— 这三者无法共用一个 svc.ServiceContext 的数据库连接池配置,强行合并会导致连接泄漏或事务跨库失败。
② 鉴权粒度不一致:用户登录接口需 JWT 校验,但健康检查接口(/healthz)必须免鉴权,而支付回调接口需验签且禁止 JWT。若全塞进一个 service,中间件链要么漏放行、要么误拦截,【middleware 配置只能作用于整个 service,无法按单个路由开关】。
③ SLA 要求差异显著:订单创建接口要求 P99
同一服务内如何合理使用 group 与 prefix
group 决定 Go 代码的包路径和 handler 目录结构;prefix 决定最终注册到 HTTP Server 上的 URL 路径前缀。二者可不同,但必须逻辑自洽。
方法一:group 与 prefix 完全一致(推荐新手)
在 user.api 中写:@server(prefix = "/user")<br>service UserService {<br> @group(user) GetUserInfo() ...<br> @group(user) UpdateUser() ...<br>}
→ 生成 handler 在 internal/handler/user/,路由为 /user/info、/user/update。清晰无歧义。
方法二:group 仅作代码组织,prefix 统一收敛(适合网关层聚合)@server(prefix = "/v1")<br>service UserService {<br> @group(auth) Login() ...<br> @group(profile) GetProfile() ...<br>}
→ handler 分别落在 internal/handler/auth/ 和 internal/handler/profile/,但路由注册为 /v1/login 和 /v1/profile。注意:【group 名不能含 /,否则 goctl 会报错退出】。
多 .api 文件协同时的引用边界
当拆出 user.api 和 order.api 后,若订单详情接口需返回嵌套的用户基本信息,不要在 order.api 里重复定义 type UserBaseInfo。
正确做法:在 types/api_types.api 中统一声明公共类型:type UserBaseInfo {<br> Id int64 `json:"id"`<br> Name string `json:"name"`<br>}
然后在 order.api 顶部 import:import "types/api_types.api"
这样 goctl 生成时会自动合并 types.go,避免字段不一致引发的序列化错误。
注意:import 路径是相对于当前 .api 文件的相对路径,不是 Go module 路径。若 order.api 在 api/order/,而 api_types.api 在 api/types/,则 import 必须写 import "../types/api_types.api"。
验证拆分是否合理的最终手段
执行 goctl api plugin -plugin goctl-swagger="swagger" -api user.api -dir ./swagger 生成 OpenAPI 文档后,打开 HTML 查看:
• 每个独立服务对应一个独立的 swagger.json 文件;
• 同一服务内所有接口的 basePath 必须相同;
• 不同服务的 info.title 必须语义明确(如 "User Service v1", "Order Service v1");
• 所有跨服务引用的 schema 必须出现在 definitions 中且名称唯一。
如果 swagger 文档里出现两个同名 schema(如都叫 User 但字段不同),说明 types 拆分越界,必须回退并重构 import 关系。



















