Go搭建高效的Gin Web目錄結(jié)構(gòu)
引言
在當(dāng)今迅速迭代的軟件開發(fā)領(lǐng)域,挑選合適的工具與框架對于項(xiàng)目能否順利推進(jìn)至關(guān)重要。Gin 框架,作為 Go 語言生態(tài)中備受青睞的 Web 開發(fā)框架,憑借其卓越的性能、簡潔的設(shè)計(jì)以及豐富的功能特性,在眾多選項(xiàng)中脫穎而出。本文旨在深入剖析如何在使用 Gin 框架的過程中,構(gòu)建一個(gè)既高效又便于管理的項(xiàng)目架構(gòu),助力開發(fā)者打造既快速響應(yīng)又易于維護(hù)的 Web 應(yīng)用程序。
一、Gin 概述
引入官網(wǎng)的描述:Gin 是一個(gè)使用 Go 語言開發(fā)的 Web 框架。 它提供類似 Martini 的 API,但性能更佳,速度提升高達(dá)40倍。 如果你是性能和高效的追求者, 你會(huì)愛上 Gin。
對比 Beego 框架,Gin 框架采用了極簡主義的方法,為追求簡單和高性能,沒有多余文件或目錄,他甚至什么也沒有,沒有集成任何中間件,一個(gè) main 文件即可啟動(dòng)一個(gè) web 服務(wù)。
正因?yàn)槿缟纤?,過分精簡對于開發(fā)一個(gè)項(xiàng)目來說,前期的項(xiàng)目搭建工作就顯得尤為重要。
二、項(xiàng)目結(jié)構(gòu)設(shè)計(jì)
有過 Java 開發(fā)經(jīng)驗(yàn)的伙伴應(yīng)該了解,SpringBoot 遵循著 MVC 的設(shè)計(jì)理念,這一套設(shè)計(jì)理念一直沿用至今,他的優(yōu)秀難以言喻,Gin 框架完全可以參照這個(gè)模式來做,如下是我個(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ù)庫連接信息、服務(wù)器設(shè)置等。
/docs
- 存放應(yīng)用的文檔,如API文檔、用戶手冊等。
/internal
- 存放應(yīng)用的內(nèi)部邏輯,這些代碼不能被外部包所引入,可根據(jù)實(shí)際需求進(jìn)而拆分目錄。
api
:包含應(yīng)用中核心的業(yè)務(wù)路由等,即URL路徑與控制器方法的映射。app
:包含應(yīng)用的核心邏輯,如初始化、啟動(dòng)等。controller
:包含控制器邏輯,處理請求并返回響應(yīng)。middleware
:存放中間件代碼,用于在請求處理流程中的特定階段執(zhí)行代碼。model
:定義應(yīng)用的數(shù)據(jù)模型,通常與數(shù)據(jù)庫表結(jié)構(gòu)對應(yīng)。repository
:實(shí)現(xiàn)數(shù)據(jù)訪問邏輯,與數(shù)據(jù)庫進(jìn)行交互。service
:實(shí)現(xiàn)業(yè)務(wù)邏輯,調(diào)用repository中的方法來處理業(yè)務(wù)需求。utils
:包含通用的工具函數(shù),這些函數(shù)可以被多個(gè)包所共享。
/pkg
- 存放第三方庫,如第三方中間件、工具庫等。
/scripts
- 存放各種腳本,如項(xiàng)目部署腳本、測試腳本等。
/tests
- 存放測試代碼,包括單元測試、集成測試等。
- 這里的目錄結(jié)構(gòu)可以根據(jù)需要自行組織,以支持不同類型的測試。
以上目錄結(jié)構(gòu)有助于清晰地分離應(yīng)用的不同部分,使得代碼更加模塊化、易于理解和維護(hù)。同時(shí),我也參照眾多優(yōu)秀開源項(xiàng)目的目錄搭建思想,使其完美遵循了Go語言的最佳實(shí)踐。
四、實(shí)戰(zhàn)
目錄搭建好后,開始填充代碼
下邊簡單實(shí)現(xiàn)集成數(shù)據(jù)庫,配置路由,啟動(dòng)服務(wù)
1. 配置config
在 config.yaml 文件下配置端口和數(shù)據(jù)庫連接,這里選擇 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ù)庫及其他的初始化統(tǒng)一放置到 app 目錄下,在這里新建 loader.go 來初始化 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ù)庫初始化 func InitializeMySQL() error { var err error // 創(chuàng)建數(shù)據(jù)庫引擎 Engine, err = xorm.NewEngine(config.Conf.Database.Driver, config.Conf.Database.Source) if err != nil { log.Error("數(shù)據(jù)庫初始化失敗: %v", err) return err } // 測試數(shù)據(jù)庫連接 if err = Engine.Ping(); err != nil { log.Error("數(shù)據(jù)庫連接失敗: %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ù)庫對應(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è)基本的查詢請求就已構(gòu)建完成
10. 啟動(dòng)項(xiàng)目
cmd 目錄下直接運(yùn)行 main 函數(shù),正常會(huì)輸出如下信息:
Listening and serving HTTP on :8080
接著訪問 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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
goland Duration 和time的區(qū)別說明
這篇文章主要介紹了goland Duration 和time的區(qū)別說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12Go語言中的匿名結(jié)構(gòu)體用法實(shí)例
這篇文章主要介紹了Go語言中的匿名結(jié)構(gòu)體用法,實(shí)例分析了匿名結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02Go語言中Gin框架使用JWT實(shí)現(xiàn)登錄認(rèn)證的方案
在如今前后端分離開發(fā)的大環(huán)境中,我們需要解決一些登陸,后期身份認(rèn)證以及鑒權(quán)相關(guān)的事情,通常的方案就是采用請求頭攜帶token的方式進(jìn)行實(shí)現(xiàn),本文給大家介紹了Go語言中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ì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12詳解如何解決golang定時(shí)器引發(fā)的id重復(fù)問題
這篇文章主要為大家詳細(xì)介紹了如何解決golang定時(shí)器引發(fā)的id重復(fù)問題,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04