为什么联犀选择 go-zero:goctl 代码生成、模板控制与 monorepo 的组合价值
约 4182 字大约 14 分钟
2026-05-16
很多人在谈后端框架选型时,关注点通常是性能、生态或者语法偏好。但对联犀这类 SaaS + IoT 平台来说,框架选型真正要解决的问题不是“能不能写服务”,而是如何在一套长期演进的代码库里,把 API、RPC、配置、代码生成、服务边界和团队协作一起管住。
联犀最终选择 go-zero,并不是因为它只是一个轻量微服务框架,而是因为它和 goctl 的组合,刚好能承载我们最重视的几件事:
- API 与 RPC 的边界清晰
- 代码生成结果可控
- 模板可以被定制,而不是只能接受框架默认产物
- monorepo / workspace 结构下,多模块协同依旧可管理
在 AI 开始深度参与编码之后,这套组合又多了一层以前没有那么突出的价值:它不只是帮助人写代码,也在帮助 AI 更稳定地写代码。
为什么是 go-zero,而不是只选一个“好用的 Go Web 框架”
如果系统只是做几个 HTTP 服务,很多 Go Web 框架都能胜任。
但联犀的目标更复杂。它同时要覆盖中台 API、物联网 API、RPC 服务、协议模块、AllInOne 宿主和若干共享基础能力。单纯的 Web 框架很难把这些边界一并组织好,最后很容易变成“服务能跑,但工程结构不断发散”。
go-zero 对联犀最大的价值,不是帮我们少写 handler,而是把“接口定义驱动代码结构”这件事做得足够彻底。
API 可以通过 .api 文件描述,RPC 可以通过 .proto 文件描述,生成代码后,handler、logic、service context、client 等结构天然分层。这样一来,系统的骨架不是靠团队成员自己临场决定,而是从定义文件开始就被固定下来。
对于长期维护的 monorepo 来说,这一点非常关键。因为真正昂贵的不是初次开发速度,而是几年之后还能不能持续保持统一风格。
goctl 的真正价值不是“省点样板代码”
很多团队提到代码生成时,理解还停留在“少写点重复代码”。这当然是收益之一,但不是联犀最看重的部分。
在联犀里,goctl 更像是一套结构约束工具。
它把接口定义、目录组织和生成结果捆在一起,让很多本来容易漂移的工程约束变成了默认路径。
例如:
- 新增 API 服务时,入口、handler、logic、types 的骨架是统一的
- 新增 RPC 服务时,client、server、logic 的分层是统一的
.api和.proto成为对外契约,而不是代码实现之后再回头补文档
这意味着新增一个服务,不需要每个人都重新思考“目录怎么摆、命名怎么定、接口怎么拆层”,而是直接沿着统一骨架前进。
在 AI 编码场景下,这种收益会被进一步放大。
AI 最大的问题通常不是不会写代码,而是当上下文太散、结构太自由、隐性约束太多时,它很容易在“局部看起来合理、全局却不一致”的方向上持续输出。goctl 提供的统一骨架,等于把很多原本只能靠人口口相传的约束,提前变成了生成结果本身。这样 AI 不是在一块完全开放的空地上自由发挥,而是在一个结构已经被稳定下来的工地里继续施工。
联犀为什么一定要用定制版 goctl
如果只是使用官方模板,很多系统也能跑起来。但联犀并不是一个只满足于“能生成代码”的项目。我们需要的是让生成结果贴合自己的架构,而不是让架构去迁就生成器。
因此联犀在实践中采用了定制版 go-zero / goctl,并对模板进行了控制。
这样做的原因很现实:
1. API 与 RPC 代码要贴合联犀自己的约束
联犀有自己固定的接口设计规范,例如:
- API 全部使用
post .api顶层聚合文件统一引入common.api@doc中必须显式标注权限、资源、动作和日志模式
这些约束如果只靠“团队自觉遵守”,时间长了一定会松。放进生成和校验链路里,反而更稳。
2. 生成结果要适配直连与融合式宿主
联犀不是纯远程 RPC 世界。
在很多服务里,同一套接口需要同时支持 grpc client 和 direct 调用。模板控制的价值,就在于让这类结构成为标准产物,而不是每个服务手工重复一遍。
3. 代码生成还承担了质量闸门作用
例如 goctl api swagger 在联犀里不仅是导出文档,更承担了接口元数据校验角色。
缺字段、值非法、权限说明不完整时,直接在生成阶段报错,比代码上线后再靠 review 补救要靠谱得多。
模板控制为什么对平台型项目特别重要
模板控制这件事,对单个小项目未必值当,但对联犀这种平台型 monorepo 非常重要。
原因在于,平台型项目不是只做一个服务,而是会不断新增:
- 中台服务
- 物联网服务
- 协议模块
- 应用子模块
如果生成骨架不受控,不同模块很快就会长成不同风格。
一开始看似只是“多了几个自由度”,最后往往演变成:
- 相似服务结构不一致
- 逻辑层和适配层职责混杂
- 新同学 onboarding 成本上升
- 自动化脚本、review 规则和运维流程越来越难统一
而模板一旦被控制住,很多组织性成本会前置一次性解决。
对 AI 编码来说,模板控制还有一个额外价值:它能显著减少“同类问题每次都要重新解释”的成本。
如果团队每次都需要告诉 AI:
- handler 应该只做解析,logic 才放业务
- 哪些字段必须出现在接口定义里
- 哪种 client 需要同时支持 grpc 和 direct
- 哪些注释、命名、返回结构才算项目规范
那么 AI 看似在写代码,实际上大量 token 都花在重新对齐项目习惯上。模板被控制住之后,很多规范不再需要在 prompt 里反复强调,而是直接体现在生成骨架里。AI 只需要聚焦当前差异化逻辑,而不是反复学习同一套项目纪律。
为什么说 monorepo 天然适合这种场景
联犀后端采用的是 Go Workspace 驱动下的 monorepo 组织,而不是多个完全独立的小仓库。
这对 go-zero / goctl 的价值释放非常明显。
为了更直观地理解这一点,可以先看联犀当前后端的真实目录骨架:
backend/
├── cmd/
│ └── allInOne/ # 一体化宿主入口
├── core/
│ ├── cmd/
│ ├── service/
│ │ ├── apisvr/
│ │ ├── syssvr/
│ │ ├── datasvr/
│ │ ├── aisvr/
│ │ └── timedsvr/
│ └── share/
├── things/
│ ├── cmd/
│ ├── service/
│ │ ├── apisvr/
│ │ ├── dmsvr/
│ │ ├── udsvr/
│ │ ├── aisvr/
│ │ ├── cardsvr/
│ │ ├── lowcodesvr/
│ │ └── viewsvr/
│ └── share/
├── share/
│ ├── clients/
│ ├── ctxs/
│ ├── eventBus/
│ ├── events/
│ ├── stores/
│ └── utils/
├── protocol/
│ ├── modbus/
│ ├── aliyun-mqtt/
│ ├── xiaozhi/
│ └── ...
├── mall/
└── go.work这棵目录树本身就已经说明了联犀的组织思路:
core、things、mall按业务域拆分share承担跨域公共层protocol承担协议族扩展cmd/allInOne承担融合式宿主入口go.work把这些模块收进同一 workspace
对 AI 来说,这种结构的意义非常大。它不是面对一个“所有代码都堆在一起的大仓库”,而是面对一个有稳定模块层级、稳定共享层和稳定服务骨架的大仓库。
第一,跨模块共享能力更自然
share 作为底层公共层,承载上下文、缓存、事件、存储、基础客户端等能力。core 和 things 在其上组合服务。
在 monorepo 里,这种共享是稳定且可见的;如果拆成多个仓库,公共层版本治理、跨仓演进和生成骨架同步都会麻烦很多。
第二,接口契约和实现可以一起演进
.api、.proto、生成客户端、服务实现、文档和测试都在同一代码库里。
这让“改接口 -> 生成代码 -> 修实现 -> 跑校验”的闭环非常顺畅,也更适合高频迭代的中台和物联网场景。
第三,workspace 比“每个子服务一个独立模块”更稳
联犀实践里一个很重要的经验就是:go.work 应尽量只声明真正需要的顶层模块,而不是把每个子服务都当成独立 module。
这也是 monorepo 能稳定运行的前提之一。否则一旦子服务目录被误加入 workspace,依赖解析、go.work.sum 和生成链路都会变得混乱。
换句话说,monorepo 天然适合联犀这种跨域平台,但前提是它必须有清晰的模块纪律,而 go-zero + goctl 正好强化了这种纪律。
AI 时代,monorepo 为什么反而更适合“让 AI 专注”
很多人会直觉认为,仓库越大,AI 越难工作,所以 monorepo 对 AI 不友好。这个判断只说对了一半。
如果 monorepo 没有边界、没有模块纪律、没有统一骨架,那它当然会让 AI 很痛苦,因为上下文一展开就是整片代码海洋,模型既不知道该看哪里,也不知道哪些约束是全局性的。
但联犀这种带有清晰模块边界的 monorepo,反而更适合 AI 参与开发,原因恰恰在于“既大,又能局部聚焦”。
第一,AI 可以先锁定具体模块,再锁定具体服务
core、things、share、protocol 的边界清晰之后,AI 不需要一上来吞下整个仓库。
它完全可以先判断当前问题属于哪个模块,再继续缩小到具体服务、具体接口、具体逻辑目录。这样上下文裁剪会自然很多,模型也更容易把注意力放在真正相关的那部分代码上。
例如:
- 权限、Hook、WebSocket 相关问题,大概率先落在
core/service或core/share - 设备接入、MQTT、时序仓储问题,大概率先落在
things/service/dmsvr - 公共上下文、事件封装、基础存储能力,优先看
share - 协议厂商适配问题,优先看
protocol/*
这类定位过程对人类工程师当然也有帮助,但对 AI 尤其重要。因为 AI 一旦能先缩小到“正确的那一块代码”,后续的 token、推理和修改范围都会稳定很多。
对 AI 来说,最昂贵的不是代码量本身,而是无效上下文。
清晰的 monorepo 结构,等于给了它一张可靠地图,让它可以逐层收缩搜索范围,而不是每次都从零开始全仓猜测。
第二,公共能力在一个仓里,AI 更容易看清真实约束
如果一个平台拆成很多仓库,AI 在处理某个服务时,往往看不到真正的共享层实现、上下文工具、公共错误模型和事件封装方式,只能凭局部信息猜测系统约束。
而在 monorepo 里,share 层、公共 client、统一上下文、基础中间件都和业务代码在同一仓库中。AI 更容易看到“这个系统真实是怎么写的”,而不是只看某个局部入口文件。
这会显著减少一种常见错误:模型在局部文件里写出了看似合理、但实际上违背全局公共能力用法的实现。
第三,monorepo 让 AI 更容易做“局部修改,不扩散重构”
联犀这种结构下,模块边界和目录职责都比较稳定,AI 更容易判断哪些地方是本次任务真正应该动的,哪些地方只是相关背景。
这对于控制改动范围特别重要。因为 AI 一旦看不清边界,就很容易做出“顺手优化一片”的扩散式修改。
清晰的 monorepo,不是让 AI 看更多,而是让 AI 更容易知道“不该看什么、不该改什么”。
goctl + 模板控制,本质上是在给 AI 提供稳定护栏
如果说 monorepo 提供的是地图,那么 goctl 和模板控制提供的就是护栏。
AI 编码时最容易出现的问题之一,是它会在不确定时自行补全模式,而这种补全往往带有模型自己的通用经验,不一定符合当前项目。
联犀通过 goctl、定制模板和生成后的校验链路,把很多本应模糊的选择变成了明确规则:
- 接口定义先行,而不是实现先行
- 目录分层先定好,而不是边写边长
- 常见 client / server / logic 骨架先生成,而不是临时手写
- 关键元数据字段必须齐全,否则生成阶段直接失败
对 AI 来说,这种环境非常重要。
因为最好的 AI 编码协作方式,不是让模型在一个完全自由的环境里“尽情发挥”,而是给它一个足够清晰、足够稳定、足够可验证的工程系统,让它把推理能力集中用在真正有差异的业务逻辑上。
换句话说,AI 时代我们更需要的,恰恰不是更随意的代码库,而是更结构化、更可生成、更可校验的代码库。
为什么这套组合特别适合 SaaS + IoT
SaaS + IoT 最大的特点,是系统一半在做中台,一半在做设备链路。
这两边既有差异,又有大量共享基础设施,例如:
- 权限上下文
- API 风格
- 事件总线
- 配置管理
- 通知与日志
- 共享客户端
如果没有一套统一的代码生成与工程结构约束,这种跨域系统很容易越长越散。
go-zero 在联犀里的价值,就是帮我们把“跨域协同”控制在同一语言、同一工程骨架、同一生成链路之下,而不是让每个子域自由长成不同框架风格。
这套选择的代价是什么
当然,选择 go-zero 并配合定制版 goctl,也不是没有代价。
最大的代价有两个。
一是需要维护自己的模板和约束
一旦走上模板控制路线,就意味着不能完全依赖上游默认行为。团队需要对生成结果负责,也要理解哪些是框架默认,哪些是项目定制。
二是要求团队尊重“定义先行”
goctl 的优势建立在接口定义足够清晰的前提上。
如果团队习惯先随手写代码,再回头补契约,这套方式反而会觉得束手束脚。
但对联犀来说,这两项代价是值得的。因为平台越大,结构一致性越值钱。
总结
联犀选择 go-zero,并不是因为它只是“一个不错的 Go 微服务框架”,而是因为它和 goctl、模板控制以及 monorepo/workspace 结构组合起来之后,刚好满足了平台型项目最需要的几件事:
- 用定义驱动结构
- 用代码生成稳定骨架
- 用模板控制固化项目约束
- 用 monorepo 组织跨域共享与协同演进
而到了 AI 深度参与研发的阶段,这套选择的价值反而更明显了:
- monorepo 让 AI 能沿着模块边界逐层聚焦,减少无效上下文
- goctl 让大量样板结构天然规范,减少 AI 自由发挥带来的漂移
- 模板控制让项目约束直接体现在骨架里,而不是每次都靠 prompt 重讲一遍
- 生成与校验链路把很多错误前移到“还没进入业务代码”之前
所以如果把问题放到今天更真实的语境里,它不只是“如何长期维护一个 SaaS + IoT 平台级代码库”,也是“如何让人和 AI 都能在同一套代码库里持续稳定地产出规范代码”。在这个意义上,go-zero + goctl + 模板控制 + monorepo 的组合,已经不只是开发效率工具链,而是一套面向 AI 协同开发时代的工程组织方式。
更新日志
2026/5/18 10:48
查看所有更新日志
43ef3-docs(blog): 新增后端与架构系列技术博客 18 篇,更新原有 7 篇于
