前言
做 Go 项目的时候,你是不是也遇到过这种情况?
- Controller、Service、DAO 全塞在一个文件里,改一个字段牵一发动全身
- 数据库切 MySQL → PostgreSQL,改了十几处
- 想加个 Redis 缓存,却发现代码里全是 init() 硬编码,根本没法换
- 测试的时候 mock 不了,因为对象全是 new 出来的,耦合太死了
这些问题我全踩过,所以干脆花了点时间,把一个 企业级 Go Web 脚手架 从 0 到 1 撸了出来。项目地址在 gitee,有需要的直接 Fork,改改模块名就能用。
核心技术选型
| 技术 | 版本 | 选它的理由 |
|---|---|---|
| Go | 1.25.0 | 必须用新不用旧 |
| Gin | 1.11.0 | 路由快、内存低、中间件生态成熟 |
| GORM | 1.31.1 | 泛型驱动支持好,链式调用优雅 |
| Viper | 1.21.0 | 配置热加载、环境变量覆盖 |
| Zap | 1.27.1 | 结构化日志,生产环境 JSON 输出 |
| Goose | 3.27.0 | SQL 迁移版本化管理,团队协作必备 |
| go-redis | 9.17.3 | Redis 客户端 |
核心设计:分层 + DI + 可插拔
1. 四层架构
HTTP Layer (Gin)
├─ Controller Layer(接受 RouterContextInterface,不直接依赖 Gin)
├─ Service Layer(业务逻辑)
├─ DAO Layer(数据访问)
└─ Model Layer(GORM)
Controller 层不直接持有 *gin.Context,而是通过 RouterContextInterface 接口接受——这意味着你可以把这套业务逻辑迁移到 Chi、Echo,甚至 gRPC,不需要改一行 Service 代码。
2. 自定义 DI 容器
没用 Uber/dig,也没用 Wire 代码生成,自己撸了一个 200 行的轻量容器:
// 通过 inject:"name" 标签自动注入
type UserService struct {
DB *gorm.DB `inject:"database"`
}
// 懒加载单例,首次 Get 时创建
svc := container.MustGet[*UserService]()
支持:
- 懒加载单例:按需初始化,不用启动就拉起所有连接
- 优先级覆盖:同名 Provider 高优先级覆盖低优先级,方便测试时替换
- 反射注入:inject:"name" 标签自动解析
- 生命周期钩子:Initializable / Destroyable 接口
3. 可插拔驱动系统
这是我觉得最有意思的部分。基于泛型 driver.Manager[T],每个基础设施层都支持多个驱动实现:
// 数据库驱动
driver := config.GetDatabaseDriver() // mysql / postgresql / sqlite / memory
// 日志驱动
driver := config.GetLoggerDriver() // development / production
// 缓存驱动
driver := config.GetCacheDriver() // redis / memory / none
切驱动只需要改一行配置 YML,不动任何业务代码。这对多环境开发(本地 SQLite、生产 MySQL)和本地调试巨有用。
数据库迁移:用 Goose 做版本化管理
之前团队里数据库变更全靠手工 SQL,经常出现「谁先跑?谁没跑?」的混乱。用 Goose 之后清爽多了:
# 创建迁移
go run cmd/migrate/main.go create create_users_table
# 执行所有迁移
go run cmd/migrate/main.go up
# 回滚最近一次
go run cmd/migrate/main.go down
# 查看状态
go run cmd/migrate/main.go status
迁移文件是标准 SQL,可读可调试。服务启动时自动执行 goose.Up,不需要单独跑迁移脚本。
优雅启停:信号处理
go run main.go start # 前台
go run main.go start -d # 守护进程
go run main.go stop # 停止
go run main.go restart # 重启
go run main.go reload # 热重载(收到 SIGHUP)
- SIGINT / SIGTERM → 优雅关闭,等现有请求处理完再退
- SIGHUP → 热重载配置,不需要重启进程
快速开始
# 克隆
git clone https://cnb.cool/mliev/open/go-web
cd go-web
# Fork 后初始化(替换模块路径)
./init.sh
# 配置
cp config.yaml.example config.yaml
vim config.yaml
# 安装依赖 & 启动
go mod tidy
go run main.go start
写在最后
这个脚手架并不是什么新框架,只是把做 Go 项目过程中积累的一些工程实践整理了一下。如果你正在从 0 到 1 搭建 Go 项目,或者想找一个「改改就能上生产」的起点,这个仓库或许值得一看。
代码还在维护,有问题欢迎提 Issue。
项目地址:https://gitee.com/muleiwu/go-web