Gin Rest

gorm 大型项目最佳实践

在使用 GORM 构建大型项目时,以下是一些最佳实践,可以帮助提高项目的可维护性和可扩展性。

项目结构

清晰的项目结构是保证项目可维护性的关键。以下是一个推荐的项目结构:

go
复制代码
├── config           // 配置文件
│   └── config.go
├── controllers      // 控制器
│   └── userController.go
├── middlewares      // 中间件
│   └── authMiddleware.go
├── models           // 模型
│   └── user.go
├── repositories     // 数据访问层
│   └── userRepository.go
├── services         // 服务层
│   └── userService.go
├── routes           // 路由
│   └── routes.go
├── utils            // 工具函数
│   └── logger.go
├── main.go          // 主程序入口
└── go.mod

配置管理

使用 Viper 或其他配置管理库来管理应用程序配置,例如数据库连接信息、环境变量等。

go
复制代码
// config/config.go
package config

import (
    "github.com/spf13/viper"
    "log"
)

func InitConfig() {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")

    if err := viper.ReadInConfig(); err != nil {
        log.Fatalf("Error while reading config file: %v", err)
    }
}

func GetDBConfig() (string, string) {
    return viper.GetString("database.driver"), viper.GetString("database.connectionString")
}

路由管理

集中管理路由,使代码更易于维护。

go
复制代码
// routes/routes.go
package routes

import (
    "github.com/gin-gonic/gin"
    "your_project/controllers"
    "your_project/middlewares"
)

func SetupRouter() *gin.Engine {
    r := gin.Default()
    r.Use(middlewares.Logger())

    // 用户路由
    userRoutes := r.Group("/users")
    {
        userRoutes.GET("/", controllers.GetUsers)
        userRoutes.POST("/", controllers.CreateUser)
        userRoutes.GET("/:id", controllers.GetUser)
        userRoutes.PUT("/:id", controllers.UpdateUser)
        userRoutes.DELETE("/:id", controllers.DeleteUser)
    }

    return r
}

中间件

中间件用于处理通用逻辑,如日志记录、认证等。

go
复制代码
// middlewares/authMiddleware.go
package middlewares

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
            c.Abort()
            return
        }

        // 验证 token
        if !isValidToken(token) {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
            c.Abort()
            return
        }

        c.Next()
    }
}

func isValidToken(token string) bool {
    // 验证 token 的逻辑
    return true
}

控制器

控制器处理 HTTP 请求和响应,将业务逻辑委托给服务层。

go
复制代码
// controllers/userController.go
package controllers

import (
    "net/http"
    "your_project/models"
    "your_project/services"
    "github.com/gin-gonic/gin"
)

func GetUsers(c *gin.Context) {
    users, err := services.GetAllUsers()
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, users)
}

func CreateUser(c *gin.Context) {
    var user models.User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    if err := services.CreateUser(&user); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusCreated, user)
}

服务层

服务层处理业务逻辑,实现具体的业务功能。

go
复制代码
// services/userService.go
package services

import (
    "your_project/models"
    "your_project/repositories"
)

func GetAllUsers() ([]models.User, error) {
    return repositories.FindAllUsers()
}

func CreateUser(user *models.User) error {
    return repositories.SaveUser(user)
}

数据访问层

数据访问层负责与数据库交互,执行 CRUD 操作。

go
复制代码
// repositories/userRepository.go
package repositories

import (
    "your_project/models"
    "gorm.io/gorm"
)

var db *gorm.DB

func InitRepository(database *gorm.DB) {
    db = database
}

func FindAllUsers() ([]models.User, error) {
    var users []models.User
    result := db.Find(&users)
    return users, result.Error
}

func SaveUser(user *models.User) error {
    return db.Create(user).Error
}

初始化和启动

在主程序入口文件中初始化配置、数据库连接、路由等。

go
复制代码
// main.go
package main

import (
    "your_project/config"
    "your_project/models"
    "your_project/repositories"
    "your_project/routes"
    "github.com/gin-gonic/gin"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

func main() {
    // 初始化配置
    config.InitConfig()

    // 初始化数据库
    driver, connectionString := config.GetDBConfig()
    db, err := gorm.Open(sqlite.Open(connectionString), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // 自动迁移
    db.AutoMigrate(&models.User{})

    repositories.InitRepository(db)

    // 设置路由
    r := routes.SetupRouter()

    // 启动服务器
    r.Run(":8080")
}

日志记录

使用统一的日志记录机制,方便调试和问题追踪。

go
复制代码
// utils/logger.go
package utils

import (
    "log"
    "os"
)

var (
    InfoLogger  *log.Logger
    ErrorLogger *log.Logger
)

func InitLogger() {
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatal("Failed to open log file:", err)
    }

    InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

在需要记录日志的地方使用:

go
复制代码
// 示例:在控制器中记录日志
func CreateUser(c *gin.Context) {
    var user models.User
    if err := c.ShouldBindJSON(&user); err != nil {
        utils.ErrorLogger.Println("Failed to bind JSON:", err)
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    if err := services.CreateUser(&user); err != nil {
        utils.ErrorLogger.Println("Failed to create user:", err)
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    utils.InfoLogger.Println("User created successfully:", user.ID)
    c.JSON(http.StatusCreated, user)
}

总结

  1. 项目结构清晰:按功能模块划分代码,便于维护和扩展。
  2. 配置管理:使用配置文件集中管理配置。
  3. 中间件:处理常见任务如认证和日志记录。
  4. 控制器、服务、数据访问层分离:控制器处理请求和响应,服务层实现业务逻辑,数据访问层与数据库交互。
  5. 日志记录:使用统一的日志记录机制,方便调试和问题追踪。
  6. 初始化和启动:在主程序入口初始化配置、数据库连接和路由。

通过遵循这些最佳实践,可以构建一个高效、可维护的大型 GORM 项目。