Gin is a fast and minimal HTTP web framework for Go. When working on larger projects, it is essential to structure the code in a modular way to improve maintainability, testability, and scalability.
1. Project Structure
A modular structure can be organized as follows:
myapp/
├── cmd/
│ └── main.go
├── internal/
│ ├── server/
│ │ └── server.go
│ ├── handler/
│ │ ├── user.go
│ │ └── product.go
│ ├── service/
│ │ ├── user.go
│ │ └── product.go
│ ├── repository/
│ │ ├── user.go
│ │ └── product.go
│ └── model/
│ ├── user.go
│ └── product.go
└── go.mod
2. Entry Point
In the main.go
file, we initialize the server:
package main
import "myapp/internal/server"
func main() {
server.Start()
}
3. Starting the Server
In the server
package we configure the router:
package server
import (
"github.com/gin-gonic/gin"
"myapp/internal/handler"
)
func Start() {
r := gin.Default()
// Register routes
handler.RegisterUserRoutes(r)
handler.RegisterProductRoutes(r)
r.Run() // default :8080
}
4. Route Handling
In the handler
package we separate routes by domain:
package handler
import (
"github.com/gin-gonic/gin"
"myapp/internal/service"
)
func RegisterUserRoutes(r *gin.Engine) {
group := r.Group("/users")
group.GET("/", getAllUsers)
group.GET("/:id", getUserByID)
}
func getAllUsers(c *gin.Context) {
users := service.GetAllUsers()
c.JSON(200, users)
}
func getUserByID(c *gin.Context) {
id := c.Param("id")
user, err := service.GetUserByID(id)
if err != nil {
c.JSON(404, gin.H{"error": "User not found"})
return
}
c.JSON(200, user)
}
5. Services and Repositories
Services encapsulate business logic, while repositories handle data access.
package service
import (
"myapp/internal/model"
"myapp/internal/repository"
)
func GetAllUsers() []model.User {
return repository.FindAllUsers()
}
func GetUserByID(id string) (model.User, error) {
return repository.FindUserByID(id)
}
package repository
import (
"fmt"
"myapp/internal/model"
)
var users = []model.User{
{ID: "1", Name: "Alice"},
{ID: "2", Name: "Bob"},
}
func FindAllUsers() []model.User {
return users
}
func FindUserByID(id string) (model.User, error) {
for _, u := range users {
if u.ID == id {
return u, nil
}
}
return model.User{}, fmt.Errorf("user not found")
}
6. Models
Finally, models describe the domain entities:
package model
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
Conclusion
Structuring a Gin application in a modular way promotes code clarity and separation of concerns. This approach is particularly useful in medium and large projects, making testing, extensions, and refactoring easier.