Go搭建高效的Gin Web目錄結(jié)構(gòu)
引言
在當(dāng)今迅速迭代的軟件開(kāi)發(fā)領(lǐng)域,挑選合適的工具與框架對(duì)于項(xiàng)目能否順利推進(jìn)至關(guān)重要。Gin 框架,作為 Go 語(yǔ)言生態(tài)中備受青睞的 Web 開(kāi)發(fā)框架,憑借其卓越的性能、簡(jiǎn)潔的設(shè)計(jì)以及豐富的功能特性,在眾多選項(xiàng)中脫穎而出。本文旨在深入剖析如何在使用 Gin 框架的過(guò)程中,構(gòu)建一個(gè)既高效又便于管理的項(xiàng)目架構(gòu),助力開(kāi)發(fā)者打造既快速響應(yīng)又易于維護(hù)的 Web 應(yīng)用程序。
一、Gin 概述
引入官網(wǎng)的描述:Gin 是一個(gè)使用 Go 語(yǔ)言開(kāi)發(fā)的 Web 框架。 它提供類似 Martini 的 API,但性能更佳,速度提升高達(dá)40倍。 如果你是性能和高效的追求者, 你會(huì)愛(ài)上 Gin。
對(duì)比 Beego 框架,Gin 框架采用了極簡(jiǎn)主義的方法,為追求簡(jiǎn)單和高性能,沒(méi)有多余文件或目錄,他甚至什么也沒(méi)有,沒(méi)有集成任何中間件,一個(gè) main 文件即可啟動(dòng)一個(gè) web 服務(wù)。
正因?yàn)槿缟纤觯^(guò)分精簡(jiǎn)對(duì)于開(kāi)發(fā)一個(gè)項(xiàng)目來(lái)說(shuō),前期的項(xiàng)目搭建工作就顯得尤為重要。
二、項(xiàng)目結(jié)構(gòu)設(shè)計(jì)
有過(guò) Java 開(kāi)發(fā)經(jīng)驗(yàn)的伙伴應(yīng)該了解,SpringBoot 遵循著 MVC 的設(shè)計(jì)理念,這一套設(shè)計(jì)理念一直沿用至今,他的優(yōu)秀難以言喻,Gin 框架完全可以參照這個(gè)模式來(lái)做,如下是我個(gè)人設(shè)計(jì)的一套架構(gòu):
├── /cmd │ └── main.go ├── /configs │ └── config.yaml ├── /docs ├── /internal │ ├── /api │ │ ├── v1 │ │ │ ├── /routes.go │ ├── /app │ │ ├── bootstrap.go │ │ ├── config.go │ │ ├── db.go │ │ └── ... │ ├── /controller │ │ ├── user_controller.go │ │ └── ... │ ├── /middleware │ │ ├── error.go │ │ └── ... │ ├── /model │ │ ├── user_entity.go │ │ └── ... │ ├── /repository │ │ ├── user_repository.go │ │ └── ... │ ├── /service │ │ ├── user_service.go │ │ └── ... │ └── /utils ├── /pkg ├── /scripts ├── /tests ├── .env ├── go.mod ├── go.sum
三、目錄職責(zé)
/cmd- 存放應(yīng)用的入口文件。
main.go:是整個(gè)應(yīng)用的入口,在這里啟動(dòng)應(yīng)用。
/configs- 存放應(yīng)用的配置文件和配置加載邏輯。
config.yaml:應(yīng)用的配置文件,通常包含數(shù)據(jù)庫(kù)連接信息、服務(wù)器設(shè)置等。
/docs- 存放應(yīng)用的文檔,如API文檔、用戶手冊(cè)等。
/internal- 存放應(yīng)用的內(nèi)部邏輯,這些代碼不能被外部包所引入,可根據(jù)實(shí)際需求進(jìn)而拆分目錄。
api:包含應(yīng)用中核心的業(yè)務(wù)路由等,即URL路徑與控制器方法的映射。app:包含應(yīng)用的核心邏輯,如初始化、啟動(dòng)等。controller:包含控制器邏輯,處理請(qǐng)求并返回響應(yīng)。middleware:存放中間件代碼,用于在請(qǐng)求處理流程中的特定階段執(zhí)行代碼。model:定義應(yīng)用的數(shù)據(jù)模型,通常與數(shù)據(jù)庫(kù)表結(jié)構(gòu)對(duì)應(yīng)。repository:實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)邏輯,與數(shù)據(jù)庫(kù)進(jìn)行交互。service:實(shí)現(xiàn)業(yè)務(wù)邏輯,調(diào)用repository中的方法來(lái)處理業(yè)務(wù)需求。utils:包含通用的工具函數(shù),這些函數(shù)可以被多個(gè)包所共享。
/pkg- 存放第三方庫(kù),如第三方中間件、工具庫(kù)等。
/scripts- 存放各種腳本,如項(xiàng)目部署腳本、測(cè)試腳本等。
/tests- 存放測(cè)試代碼,包括單元測(cè)試、集成測(cè)試等。
- 這里的目錄結(jié)構(gòu)可以根據(jù)需要自行組織,以支持不同類型的測(cè)試。
以上目錄結(jié)構(gòu)有助于清晰地分離應(yīng)用的不同部分,使得代碼更加模塊化、易于理解和維護(hù)。同時(shí),我也參照眾多優(yōu)秀開(kāi)源項(xiàng)目的目錄搭建思想,使其完美遵循了Go語(yǔ)言的最佳實(shí)踐。
四、實(shí)戰(zhàn)
目錄搭建好后,開(kāi)始填充代碼
下邊簡(jiǎn)單實(shí)現(xiàn)集成數(shù)據(jù)庫(kù),配置路由,啟動(dòng)服務(wù)
1. 配置config
在 config.yaml 文件下配置端口和數(shù)據(jù)庫(kù)連接,這里選擇 xorm:
# 基礎(chǔ)配置 app: port: 8080 database: driver: mysql source: root:123456@tcp(127.0.0.1:3306)/xxx_table?charset=utf8mb4&parseTime=True&loc=Local
在 config.go 下解析配置
package config
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
App AppConfig `yaml:"app" mapstructure:"app"`
Database DatabaseConfig `yaml:"database" mapstructure:"database"`
}
type AppConfig struct {
Port int `mapstructure:"port"`
}
type DatabaseConfig struct {
Driver string `yaml:"driver" mapstructure:"driver"`
Source string `yaml:"source" mapstructure:"source"`
}
var Conf *Config
// LoadConfig 加載配置文件
func LoadConfig() error {
// 設(shè)置配置文件路徑和名稱
viper.AddConfigPath("./configs")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
// 讀取配置文件
err = viper.ReadInConfig()
if err != nil {
return fmt.Errorf("讀取配置文件失敗: %v", err)
}
// 將配置文件內(nèi)容解析到 Conf 變量中
Conf = &Config{}
err = viper.Unmarshal(Conf)
if err != nil {
return fmt.Errorf("解析配置文件失敗: %v", err)
}
return nil
}
2. 配置init
數(shù)據(jù)庫(kù)及其他的初始化統(tǒng)一放置到 app 目錄下,在這里新建 loader.go 來(lái)初始化 mysql,但是為了之后方便管理,我們另單獨(dú)創(chuàng)建 db.go 文件:
如需要加載其他如 redis,那就新建 redis.go 文件
package app
import (
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
log "github.com/sirupsen/logrus"
"yourProject/config"
)
var Engine *xorm.Engine
// InitializeMySQL 數(shù)據(jù)庫(kù)初始化
func InitializeMySQL() error {
var err error
// 創(chuàng)建數(shù)據(jù)庫(kù)引擎
Engine, err = xorm.NewEngine(config.Conf.Database.Driver, config.Conf.Database.Source)
if err != nil {
log.Error("數(shù)據(jù)庫(kù)初始化失敗: %v", err)
return err
}
// 測(cè)試數(shù)據(jù)庫(kù)連接
if err = Engine.Ping(); err != nil {
log.Error("數(shù)據(jù)庫(kù)連接失敗: %v", err)
return err
}
return nil
}
app.go 中調(diào)用 InitializeMySQL()
package app
import (
"fmt"
)
// InitializeAll 初始化所有模塊
func InitializeAll() error {
err := InitializeMySQL()
if err != nil {
return fmt.Errorf("MySQL初始化錯(cuò)誤: %v", err)
}
return nil
}
3. 配置model
在 model 下新建 user_entity.go,注意:這個(gè)需要和數(shù)據(jù)庫(kù)對(duì)應(yīng)
package model
type User struct {
Id int64 `xorm:"pk autoincr 'id'"`
UserID int64 `xorm:"not null 'user_id'"`
Password string `xorm:"varchar(50) not null 'password'"`
UserName string `xorm:"varchar(30) 'user_name'"`
Email string `xorm:"varchar(50) 'email'"`
PhoneNumber int64 `xorm:"'phone_number'"`
Sex string `xorm:"char(1) 'sex'"`
Remark string `xorm:"varchar(500) 'remark'"`
}
// TableName 方法用于返回表名
func (u User) TableName() string {
return "user"
}
4. 配置controller
在 controller 下新建 user_controller.go
package controller
import (
"your_project/internal/service"
"github.com/gin-gonic/gin"
"net/http"
)
type UserController struct {
UserService *service.UserService
}
func NewUserController(UserService *service.UserService) *UserController {
return &UserController{UserService: UserService}
}
func (uc *UserController) GetUsers(c *gin.Context) {
users, err := uc.UserService.GetUsers()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users"})
return
}
c.JSON(http.StatusOK, gin.H{"users": users})
}
5. 配置service
在 service 下新建 user_service.go
package service
import (
"your_project/internal/model"
"your_project/internal/repository"
"github.com/go-xorm/xorm"
)
type UserService struct {
userRepo *repository.UserRepository
}
func NewUserService(engine *xorm.Engine) *UserService {
return &UserService{userRepo: repository.NewUserRepository(engine)}
}
func (us *UserService) GetUsers() ([]*models.User, error) {
return us.userRepo.GetUsers()
}
6. 配置repository
在 repository 下新建 user_repo.go
package repository
import (
"your_project/internal/model"
"github.com/go-xorm/xorm"
)
type UserRepository struct {
engine *xorm.Engine
}
func NewUserRepository(engine *xorm.Engine) *UserRepository {
return &UserRepository{engine: engine}
}
// GetUsers 獲取所有用戶
func (r *UserRepository) GetUsers() ([]*model.User, error) {
var users []*model.User
err := r.engine.Table(model.User{}.TableName()).Find(&users)
return users, err
}
7. 配置api
routes.go 中設(shè)置路由,這里設(shè)置路由組,為方便日后迭代
package v1
import (
"github.com/gin-gonic/gin"
"github.com/go-xorm/xorm"
"your_project/internal/controller"
"your_project/internal/service"
)
func SetupRoutes(r *gin.Engine, engine *xorm.Engine) {
// 定義用戶路由組
user := r.Group("/user")
{
// 創(chuàng)建 UserService 實(shí)例
UserService := service.NewUserService(engine)
// 創(chuàng)建 UserController 實(shí)例
UserController := controller.NewUserController(UserService)
user.GET("/", UserController.GetUsers)
}
}
8. 配置bootstrap
package app
import (
"fmt"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"your_project/config"
"your_project/internal/api/v1"
"your_project/internal/app"
)
func Start() {
// 加載配置文件
err := config.LoadConfig()
if err != nil {
log.Error("配置文件加載錯(cuò)誤: %v", err)
return
}
// 初始化所有模塊
err = InitializeAll()
if err != nil {
log.Error("模塊初始化錯(cuò)誤: %v", err)
return
}
r := gin.Default()
v1.SetupRoutes(r, Engine)
err = r.Run(fmt.Sprintf(":%d", config.Conf.App.Port))
if err != nil {
log.Error("服務(wù)啟動(dòng)錯(cuò)誤: %v", err)
return
}
}
9. 配置main
package app
import "your_project/internal/app"
func main() {
app.Start()
}
截至這里,一個(gè)基本的查詢請(qǐng)求就已構(gòu)建完成
10. 啟動(dòng)項(xiàng)目
cmd 目錄下直接運(yùn)行 main 函數(shù),正常會(huì)輸出如下信息:
Listening and serving HTTP on :8080
接著訪問(wèn) http://localhost:8080/user 正常查詢結(jié)果回顯 json 如下:
{
"users": [
{
"Id": 1,
"UserID": "000001",
"Password": "123456",
...
}
]
}
到此這篇關(guān)于Go搭建高效的Gin Web目錄結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)Gin Web目錄結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Go語(yǔ)言啟動(dòng)Redis的實(shí)例詳解
這篇文章主要為大家介紹了Go語(yǔ)言中一個(gè)可以用來(lái)啟動(dòng)?redis-server?的開(kāi)源庫(kù)?github.com/stvp/tempredis,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-01-01
golang之判斷元素是否在數(shù)組內(nèi)問(wèn)題
這篇文章主要介紹了golang之判斷元素是否在數(shù)組內(nèi)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2020-12-12
goland Duration 和time的區(qū)別說(shuō)明
這篇文章主要介紹了goland Duration 和time的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Go語(yǔ)言微服務(wù)中實(shí)現(xiàn)鏈路追蹤
在微服務(wù)架構(gòu)中,鏈路追蹤技術(shù)可以幫助我們跟蹤請(qǐng)求在各個(gè)服務(wù)之間的傳播路徑,本文就來(lái)介紹一下Go語(yǔ)言微服務(wù)中實(shí)現(xiàn)鏈路追蹤,感興趣的可以了解一下2024-12-12
Go語(yǔ)言中的匿名結(jié)構(gòu)體用法實(shí)例
這篇文章主要介紹了Go語(yǔ)言中的匿名結(jié)構(gòu)體用法,實(shí)例分析了匿名結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
Go語(yǔ)言中Gin框架使用JWT實(shí)現(xiàn)登錄認(rèn)證的方案
在如今前后端分離開(kāi)發(fā)的大環(huán)境中,我們需要解決一些登陸,后期身份認(rèn)證以及鑒權(quán)相關(guān)的事情,通常的方案就是采用請(qǐng)求頭攜帶token的方式進(jìn)行實(shí)現(xiàn),本文給大家介紹了Go語(yǔ)言中Gin框架使用JWT實(shí)現(xiàn)登錄認(rèn)證的方案,需要的朋友可以參考下2024-11-11
使用Go module和GoLand初始化一個(gè)Go項(xiàng)目的方法
這篇文章主要介紹了使用Go module和GoLand初始化一個(gè)Go項(xiàng)目,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
詳解如何解決golang定時(shí)器引發(fā)的id重復(fù)問(wèn)題
這篇文章主要為大家詳細(xì)介紹了如何解決golang定時(shí)器引發(fā)的id重復(fù)問(wèn)題,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04

