编程 手把手带你撸一个生产级 Go Web 脚手架:分层 + DI + 可插拔驱动

2026-04-08 10:17:43 +0800 CST views 6

前言

做 Go 项目的时候,你是不是也遇到过这种情况?

  • Controller、Service、DAO 全塞在一个文件里,改一个字段牵一发动全身
  • 数据库切 MySQL → PostgreSQL,改了十几处
  • 想加个 Redis 缓存,却发现代码里全是 init() 硬编码,根本没法换
  • 测试的时候 mock 不了,因为对象全是 new 出来的,耦合太死了

这些问题我全踩过,所以干脆花了点时间,把一个 企业级 Go Web 脚手架 从 0 到 1 撸了出来。项目地址在 gitee,有需要的直接 Fork,改改模块名就能用。


核心技术选型

技术版本选它的理由
Go1.25.0必须用新不用旧
Gin1.11.0路由快、内存低、中间件生态成熟
GORM1.31.1泛型驱动支持好,链式调用优雅
Viper1.21.0配置热加载、环境变量覆盖
Zap1.27.1结构化日志,生产环境 JSON 输出
Goose3.27.0SQL 迁移版本化管理,团队协作必备
go-redis9.17.3Redis 客户端

核心设计:分层 + 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

复制全文 生成海报 Go Gin Web DI 脚手架

推荐文章

GROMACS:一个美轮美奂的C++库
2024-11-18 19:43:29 +0800 CST
在 Docker 中部署 Vue 开发环境
2024-11-18 15:04:41 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
Vue3中怎样处理组件引用?
2024-11-18 23:17:15 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
什么是Vue实例(Vue Instance)?
2024-11-19 06:04:20 +0800 CST
Vue 3 中的 Watch 实现及最佳实践
2024-11-18 22:18:40 +0800 CST
jQuery中向DOM添加元素的多种方法
2024-11-18 23:19:46 +0800 CST
goctl 技术系列 - Go 模板入门
2024-11-19 04:12:13 +0800 CST
MySQL死锁 - 更新插入导致死锁
2024-11-19 05:53:50 +0800 CST
在 Rust 中使用 OpenCV 进行绘图
2024-11-19 06:58:07 +0800 CST
如何使用go-redis库与Redis数据库
2024-11-17 04:52:02 +0800 CST
手机导航效果
2024-11-19 07:53:16 +0800 CST
程序员出海搞钱工具库
2024-11-18 22:16:19 +0800 CST
Go的父子类的简单使用
2024-11-18 14:56:32 +0800 CST
程序员茄子在线接单