现在大家都使用的的GIN和GORM开发架构是怎么样的?
目前在 Go 社区中,基于 Gin 和 GORM 的主流微服务开发架构,最推崇的是 "工程化分层"。它在你的基础上,通过引入 Logic/Service 层 和 Repository 层,彻底解决代码臃肿和难以测试的问题。
以下是目前最流行的标准架构拆解:
1. 核心分层架构图
数据流向遵循:请求 → Handler → Logic → Repository → Database。
internal/
├── handler/ # (入口层) 负责 Gin 的参数绑定、简单校验、返回 JSON
├── logic/ # (业务层) 核心是大脑!组合业务步骤,不涉及具体 SQL
├── repository/ # (持久层) 只有这里出现 GORM,负责具体的增删改查
├── model/ # (模型层) 定义 GORM 结构体(Table Schema)
└── middleware/ # (中间件) 权限校验、日志记录
2. 各层代码示例(以"用户充值"为例)
第一层:model (定义表结构)
package model
type User struct {
ID uint `gorm:"primaryKey"`
Balance int64 `gorm:"column:balance"` // 余额
}
第二层:repository (数据操作 - 只有它认识 GORM)
package repository
import "your-project/internal/model"
import "gorm.io/gorm"
type UserRepo struct {
db *gorm.DB
}
func (r *UserRepo) UpdateBalance(id uint, amount int64) error {
// 纯粹的数据库操作
return r.db.Model(&model.User{}).Where("id = ?", id).
Update("balance", gorm.Expr("balance + ?", amount)).Error
}
第三层:logic (业务核心 - 这里处理复杂逻辑)
package logic
import "your-project/internal/repository"
type UserLogic struct {
repo *repository.UserRepo
}
func (l *UserLogic) Recharge(userId uint, money int64) error {
// 1. 这里处理业务规则:比如充值不能小于 0
if money <= 0 { return errors.New("金额错误") }
// 2. 调用数据层
return l.repo.UpdateBalance(userId, money)
}
第四层:handler (Gin 入口 - 只管翻译)
package handler
import "your-project/pkg/res" // 你之前定义的统一返回
func RechargeHandler(c *gin.Context) {
var req struct { UserId uint; Amount int64 }
c.ShouldBindJSON(&req) // 参数解析
// 调用 Logic 层
err := logic.NewUserLogic().Recharge(req.UserId, req.Amount)
if err != nil {
c.JSON(200, res.Error(500, err.Error()))
return
}
c.JSON(200, res.Success(nil)) // 统一格式返回
}
3. 为什么这是主流?
- GORM 不外溢:GORM 的代码只出现在
repository或infra。如果你哪天想换sqlx或ent,只需要改底层,不需要动logic和handler。 - Logic 层可复用:
- API 调用:通过
handler调logic。 - 定时任务:通过
cron调logic。 - 自检脚本:通过
test/client调logic。
- API 调用:通过
- 并发安全与事务:主流做法是在
logic层通过闭包或 Context 处理事务,确保业务的原子性。
4. 针对你现状的调整建议
- 将
infra细化:你现在的infra可能只是初始化 DB 连接。建议在infra下建一个repository子目录,专门写 GORM 的查询。 - 增加
logic文件夹:把你handler里那些复杂的if-else、计算、多步 SQL 写入全部挪到logic里。 - 统一返回:继续使用你放在
pkg/res下的结构体,这是非常地道的做法。
避坑指南:在 GORM 开发中,千万不要把 *gin.Context 传进 logic 层或数据库层,这会导致你的业务逻辑和 HTTP 框架死死锁定。