GO分层架构【2】使用GIN与GORM

现在大家都使用的的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. 为什么这是主流?

  1. GORM 不外溢:GORM 的代码只出现在 repository 或 infra。如果你哪天想换 sqlx 或 ent,只需要改底层,不需要动 logic 和 handler。
  2. Logic 层可复用:
    • API 调用:通过 handler 调 logic。
    • 定时任务:通过 cron 调 logic。
    • 自检脚本:通过 test/client 调 logic。
  3. 并发安全与事务:主流做法是在 logic 层通过闭包或 Context 处理事务,确保业务的原子性。

4. 针对你现状的调整建议

  • 将 infra 细化:你现在的 infra 可能只是初始化 DB 连接。建议在 infra 下建一个 repository 子目录,专门写 GORM 的查询。
  • 增加 logic 文件夹:把你 handler 里那些复杂的 if-else、计算、多步 SQL 写入全部挪到 logic 里。
  • 统一返回:继续使用你放在 pkg/res 下的结构体,这是非常地道的做法。

避坑指南:在 GORM 开发中,千万不要把 *gin.Context 传进 logic 层或数据库层,这会导致你的业务逻辑和 HTTP 框架死死锁定。

相关推荐
FungLeo1 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt
FungLeo1 天前
成为全栈·Go 后端篇·重写之前先锁基线:契约、Node 版本与行为清单
golang·node.js·go·openapi·契约测试·成为全栈
小满zs2 天前
Go语言第十七章(反射)
后端·go
小满zs2 天前
Go语言第十五章(JSON序列化)
后端·go
小满zs2 天前
Go语言第十六章(自定义类型)
后端·go
w-w0w-w2 天前
Go语言学习day1
go
FungLeo2 天前
成为全栈·Go 后端篇·契约一致性与差分测试:两套实现如何互相校验
golang·node.js·go·合约测试·差分测试·成为全栈·api兼容
vipxieliang2 天前
Go 1.25 Green Tea GC 深度解析:以页为单位的垃圾回收新范式
go
孔令飞2 天前
AI 时代岗位调研分析:调研了近3000份AI岗位描述,分享下调研结果
云原生·go·ai编程
我不会起名字3222 天前
Redis 从 0 到 1(十二):先删缓存还是先更新库?4 种方案的 Go 并发实测
redis·go·并发·延迟双删·缓存一致性