介绍
matex 取「中间偏简」:不做全家桶,也不做"迁移专用"脚手架。它把企业级项目里最常用、 最通用的部分固化下来,把不适用的部分明确交给外部(K8s、网关)或 AI(skills)。
它解决的问题
每个 Go 服务都在重复回答同样几个问题,而答案通常散落在各人的习惯里:
- 目录怎么分,业务域放在哪,装配写在哪;
- 数据库、Redis、Kafka 的句柄怎么传进业务代码,什么时候判空;
- handler 返回什么,错误怎么变成状态码,日志怎么带 trace id;
- 怎么启动、怎么优雅退出、怎么热更配置。
matex 把这些答案固定成一套约定与一套薄封装,并用工具保证它们不被绕开。剩下的部分 (业务逻辑本身)它不碰——那是你和 AI 的事。
一个部署单元长什么样
type Service interface {
Name() string // 配置段 key、日志与指标前缀
Setup(ctx context.Context, env *Env) error // 解码服务段、装配 domain
BuildRouter(s *httpx.Server) error // 注册路由
}
verticle.Run 把其余全部接管:加载配置 → 初始化日志 → 灌功能开关 →
建链路追踪 → 按配置建 db/redis/memcache/kafka/auth → 调 Setup →
建 HTTP server 与路由 → 运行 → 信号触发优雅退出。
"配了才建"是什么意思
基础设施句柄对业务是私有的,一律经 Env 访问器读取。没有配置段就没有
这个依赖,此时它是 nil,而访问器让这件事显式:
// 可选依赖:逗号-ok
if db, ok := env.DB(); ok {
deps.DB = db
}
// 必需依赖:缺失即启动失败
db, err := env.RequireDB()
好处是一个只配了 http: 段的服务能零依赖起来,而它不会为 Kafka、
链路追踪、认证付出任何代价——那些代码路径根本不存在。make run-min
跑的就是这种最小服务。
刻意不做什么
舍弃和选择同样重要,理由写在下面这张表里。
| 舍弃 | 理由 |
|---|---|
| ORM |
SQL 直写 + QueryOne[T] 泛型扫描已经够用;ORM 增加黑盒与
逃逸面,也让 review 变难。
|
| gin / echo / chi |
Go 1.22 的 ServeMux 原生支持方法路由与路径参数;
httpx 补齐统一响应与观测。再引第三方路由只会增加依赖面与分叉。
|
| 服务发现 / 注册中心 | K8s 拓扑 + 网关单一入口已经解决寻址;静态 DNS + 环境变量足够。 |
| 默认开启的重治理 | 熔断、限流、幂等、链路追踪都有薄封装,但一律 opt-in——脚手架不替 使用者决定开启重治理,也不把开销强加给一个只想跑 HTTP 的服务。 |
| 配置热更时重建设施 | 热更只回调服务段配置,不重建 db/redis 连接——连接池的生命周期与配置的 生命周期本来就不同,混在一起复杂度爆炸。 |
| 代码生成器二进制 | 生成器是"会过时的约定",skill 是"给 AI 的约定"。模板随仓库演进, 不给用户多一个要与库版本对齐的工具。 |
提供了、但默认关闭的能力
上一条反对的是默认开启的重治理,不是这些能力本身。它们都在脚手架里, 但全部 opt-in:不配对应的配置段、不在代码里显式挂载,就完全不存在。
| 能力 | 开关 | 为什么留在服务内,而不是只靠网关 / K8s |
|---|---|---|
| 限流 | 显式 Use |
网关按路由/租户限流,粒度粗;服务内能按用户、API key 这类业务维度。 |
| 熔断 / 重试 | 包住具体调用 | 网关看不到"本服务到某个下游"的失败率;重试本来就是出站的事。 |
| 幂等 | Use + Idempotency-Key |
网关无从判断"这两个请求是同一笔业务"。 |
| 链路追踪 | trace.enabled |
跨服务因果链,网关只覆盖入口。 |
| 缓存模式 | 调用处 | 防击穿、防雪崩是数据访问层的职责。 |
| 定时任务 | env.Block |
网关不跑定时任务;多副本选主是业务语义。 |
| 功能开关 / 审计 | feature: 段、audit.Record |
开关决定本进程走哪条代码路径;审计要记录业务语义(哪笔订单、哪个用户), 只有服务自己知道。 |
| pprof | http.pprof |
排障必需,但 heap 里可能有凭证,故默认关且只该在受信网络暴露。 |
一致性由工具保证,而不是纪律
-
不 import 驱动库:pgx、go-sql-driver/mysql、go-redis、franz-go、gocql…
一律经
pkg/core/*。由 golangci-lint 的 depguard 规则强制, 违反则 CI 失败。 - 不 import Web 框架:第二条 depguard 规则,业务代码与示例都适用。
-
无 Docker 也能跑全量测试:SQLite、miniredis、kfake、进程内 gRPC·rpcx,
所以
make ci能验证全部 20 个示例。
为什么这件事值得写进 lint 而不是文档:约定只要靠自觉,三个月后就会有一处
import "github.com/redis/go-redis/v9" 出现在 handler 里,
而从此"基础设施入口唯一"就不再成立。
代码生成交给 AI
仓库里的 AGENTS.md 加 skills/ 下的 15 个 skill 就是生成器。
它们随代码演进(改了 httpx 的中间件签名,skill 一起改),而且不要求
使用者额外装一个 CLI 来对齐版本。
npx skills add smallnest/matex --all # 一次装齐 15 个 skill
之后用自然语言描述意图即可:"新增一个订单业务域"、 "接 postgres 并加 Redis 缓存"、"给支付接口加幂等和限流"。 每个 skill 都给出该能力的配置、代码位置、测试写法与常见坑。
下一步
- 快速开始:三条命令把服务跑起来。
- 架构总览:启动序列与分层依赖方向。
- docs/design.md:上面这些取舍的完整论证。