Strutturare un'app Go modulare con Gin

Gin è un framework HTTP web per Go veloce e minimale. Quando si lavora su progetti di una certa dimensione, è fondamentale strutturare il codice in modo modulare per migliorarne la manutenibilità, la testabilità e la scalabilità.

1. Struttura del progetto

Una struttura modulare può essere organizzata come segue:

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. Punto di ingresso

Nel file main.go, inizializziamo il server:

package main

import "myapp/internal/server"

func main() {
    server.Start()
}

3. Avvio del server

Nel pacchetto server configuriamo il 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. Gestione delle route

Nel pacchetto handler separiamo le route per dominio:

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. Servizi e repository

I servizi incapsulano la logica di business, mentre i repository gestiscono l'accesso ai dati.

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 "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. Modelli

Infine, i modelli descrivono le entità del dominio:

package model

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

Conclusione

Strutturare un'applicazione Gin in modo modulare favorisce la chiarezza del codice e la separazione delle responsabilità. Questo approccio è particolarmente utile in progetti di medie e grandi dimensioni, facilitando testing, estensioni e refactoring.

Torna su