Below is an example of a folder structure often used with Gin in large-scale projects. Gin itself does not enforce a strict layout, because it follows a minimalistic philosophy. However, the community usually follows certain best practices to keep the code organized.
my-gin-app/
├── cmd/
│ └── server/
│ └── main.go
├── config/
│ └── config.go
├── controllers/ (or handlers/)
│ └── user_controller.go
├── middlewares/
│ └── auth.go
├── models/
│ └── user.go
├── routes/
│ └── router.go
├── services/ (business logic)
│ └── user_service.go
├── repository/ (DB access, optional)
│ └── user_repository.go
├── go.mod
└── go.sum
Directory Overview (Suggested)
-
cmd/server/main.go
- The application’s entry point.
- Initializes the Gin engine, loads configs, sets up routes, etc.
- Placing
main.goinsidecmd/server/is a common Go convention to keep the root clean.
-
config/
- Handles configuration (e.g., reading
.env, using Viper, etc.). - For instance,
config.gomight havefunc InitConfig()to load environment variables or database connection info.
- Handles configuration (e.g., reading
-
controllers/ (or handlers/)
- Houses functions that handle requests (CRUD, login, etc.).
- Example:
user_controller.gohasfunc GetUsers(c *gin.Context)andfunc CreateUser(c *gin.Context).
-
middlewares/
- Contains middleware (auth, logging, recovery, etc.).
- Example:
auth.gowithfunc AuthMiddleware() gin.HandlerFunc.
-
models/
- Contains structs mapping to database tables (e.g.,
UserwithID, Name, Email...). - If using GORM, add the appropriate tags (
gorm:"...").
- Contains structs mapping to database tables (e.g.,
-
routes/
-
Contains a
SetupRouter()function to group and register routes, apply middleware, etc. -
Example:
package routes import ( "github.com/gin-gonic/gin" "my-gin-app/controllers" "my-gin-app/middlewares" ) func SetupRouter() *gin.Engine { r := gin.Default() r.Use(middlewares.LoggerMiddleware()) user := r.Group("/users") { user.GET("/", controllers.GetUsers) user.POST("/", controllers.CreateUser) } return r }
-
-
services/
- Contains business logic (e.g.,
user_service.gofor email sending, domain rules, etc.). - Your controllers can remain thin, delegating heavier tasks to these service functions.
- Contains business logic (e.g.,
-
repository/ (optional)
- Separates all database interactions (queries) from services, promoting cleaner architecture.
- In simpler projects, you can skip this folder and keep queries in services.
-
go.mod / go.sum
- Standard Go files for dependency management.
-
(Additional directories)
- migrations/ (if using Goose or GORM migrations).
- test/ (unit or integration tests).
- docs/ (API documentation, Swagger, etc.).
- scripts/ (CI/CD scripts).
Mini Example
cmd/server/main.go
package main
import (
"log"
"my-gin-app/config"
"my-gin-app/routes"
)
func main() {
// 1. Initialize config, DB, etc.
config.InitConfig()
// 2. Set up Gin router
r := routes.SetupRouter()
// 3. Run
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
config/config.go
package config
import (
"fmt"
"os"
// import "github.com/joho/godotenv" if you need to load .env
// import GORM libraries if you plan to connect to a DB
)
func InitConfig() {
// For example: load environment variables
// godotenv.Load()
fmt.Println("Config loaded, database connected... (placeholder)")
}
models/user.go
package models
import "time"
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:255"`
Email string `gorm:"size:255;unique"`
CreatedAt time.Time
UpdatedAt time.Time
}
controllers/user_controller.go
package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
"my-gin-app/services"
)
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 input struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user, err := services.CreateUser(input.Name, input.Email)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
}
services/user_service.go
package services
import (
"fmt"
"my-gin-app/models"
)
// Mocked data for illustration
func GetAllUsers() ([]models.User, error) {
return []models.User{
{ID: 1, Name: "Alice", Email: "[email protected]"},
{ID: 2, Name: "Bob", Email: "[email protected]"},
}, nil
}
func CreateUser(name, email string) (models.User, error) {
if email == "" {
return models.User{}, fmt.Errorf("email is required")
}
// Suppose we insert into DB and return the created record
user := models.User{
ID: 3,
Name: name,
Email: email,
}
return user, nil
}
routes/router.go
package routes
import (
"github.com/gin-gonic/gin"
"my-gin-app/controllers"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
userGroup := r.Group("/users")
{
userGroup.GET("/", controllers.GetUsers)
userGroup.POST("/", controllers.CreateUser)
}
return r
}
Conclusion
- Gin does not provide a default or strict folder structure. Instead, it trusts developers to organize the project.
- For mid- to large-sized projects, many people separate controllers, services, models, routes, middlewares, etc., to keep the codebase maintainable.
- The example above is a commonly used pattern in the Go community:
- cmd/server/: main entry point
- config/: app configurations (env, DB)
- controllers/ (or handlers/): handling HTTP requests
- services/: business/domain logic
- models/: database schemas (e.g., with GORM)
- routes/: routing setup
- middlewares/: shared logic for requests (JWT, logging, etc.)
- repository/: optional layer for DB queries
- go.mod/go.sum: module dependencies
Feel free to modify this structure according to your team’s needs and project size.
No comments:
Post a Comment