Struttura di un Progetto Go: Pratiche e Pattern
Struttura i tuoi progetti Go per scalabilità e chiarezza
Strutturare un progetto Go in modo efficace è fondamentale per la manutenibilità a lungo termine, la collaborazione in team e la scalabilità. A differenza dei framework che impongono layout di directory rigidi, Go accetta la flessibilità—ma con questa libertà arriva anche la responsabilità di scegliere modelli che soddisfano i bisogni specifici del tuo progetto.

Comprendere la filosofia di Go sulla struttura dei progetti
La filosofia di progettazione minimalista di Go si estende all’organizzazione dei progetti. La lingua non impone una struttura specifica, ma si fida che gli sviluppatori prendano decisioni informate. Questo approccio ha portato la comunità a sviluppare diversi modelli provati, da layout semplici per piccoli progetti a architetture sofisticate per sistemi aziendali.
Il principio chiave è semplicità prima, complessità quando necessaria. Molti sviluppatori cadono nella trappola di sovra-ingegnerizzare la struttura iniziale, creando directory profondamente annidate e astrazioni premature. Inizia con ciò di cui hai bisogno oggi, e ristruttura man mano che il tuo progetto cresce.
Quando dovresti usare la directory internal/ rispetto a pkg/?
La directory internal/ ha uno scopo specifico nel design di Go: contiene pacchetti che non possono essere importati da progetti esterni. Il compilatore di Go impone questa restrizione, rendendo internal/ perfetto per logica privata dell’applicazione, regole aziendali e utilità non destinate al riutilizzo al di fuori del tuo progetto.
La directory pkg/, d’altro canto, segnala che il codice è destinato al consumo esterno. Posiziona il codice solo qui se stai costruendo una libreria o componenti riutilizzabili che altri devono importare. Molti progetti non necessitano affatto di pkg/—se il tuo codice non è consumato esternamente, mantenerlo alla radice o in internal/ è più pulito. Quando si costruiscono librerie riutilizzabili, considera l’uso di Go generics per creare componenti tipo-sicuri e riutilizzabili.
La Struttura Standard di un Progetto Go
Il modello più riconosciuto è golang-standards/project-layout, anche se è importante notare che non è uno standard ufficiale. Ecco come appare una struttura tipica:
myproject/
├── cmd/
│ ├── api/
│ │ └── main.go
│ └── worker/
│ └── main.go
├── internal/
│ ├── auth/
│ ├── storage/
│ └── transport/
├── pkg/
│ ├── logger/
│ └── crypto/
├── api/
│ └── openapi.yaml
├── config/
│ └── config.yaml
├── scripts/
│ └── deploy.sh
├── go.mod
├── go.sum
└── README.md
La directory cmd/
La directory cmd/ contiene i punti di ingresso dell’applicazione. Ogni sottodirectory rappresenta un eseguibile separato. Ad esempio, cmd/api/main.go costruisce il tuo server API, mentre cmd/worker/main.go potrebbe costruire un processore di lavori in background.
Pratica consigliata: Mantieni i file main.go il più minimi possibile—solo quanto basta per collegare le dipendenze, caricare la configurazione e avviare l’applicazione. Tutta la logica sostanziale appartiene a pacchetti importati da main.go.
// cmd/api/main.go
package main
import (
"log"
"myproject/internal/server"
"myproject/internal/config"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatal(err)
}
srv := server.New(cfg)
if err := srv.Start(); err != nil {
log.Fatal(err)
}
}
La directory internal/
Questo è il luogo dove vive il tuo codice privato dell’applicazione. Il compilatore di Go impedisce a qualsiasi progetto esterno di importare pacchetti all’interno di internal/, rendendolo ideale per:
- Logica aziendale e modelli di dominio
- Servizi dell’applicazione
- API interne e interfacce
- Repository del database (per scegliere l’ORM giusto, vedi il nostro confronto degli ORM Go per PostgreSQL)
- Logica di autenticazione e autorizzazione
Organizza internal/ per funzionalità o dominio, non per livello tecnico. Invece di internal/handlers/, internal/services/, internal/repositories/, preferisci internal/user/, internal/order/, internal/payment/ dove ogni pacchetto contiene i propri handler, servizi e repository.
Dovrei iniziare con una struttura di directory complessa?
Assolutamente no. Se stai costruendo uno strumento piccolo o un prototipo, inizia con:
myproject/
├── main.go
├── go.mod
└── go.sum
Man mano che il tuo progetto cresce e identifichi raggruppamenti logici, introduce le directory. Potresti aggiungere un pacchetto db/ quando la logica del database diventa sostanziale, o un pacchetto api/ quando i gestori HTTP moltiplicano. Lascia che la struttura emerga naturalmente invece di imporla in anticipo.
Strutture piatte vs. annidate: trovare un equilibrio
Uno degli errori più comuni nella struttura dei progetti Go è l’eccessivo annidamento. Go favorisce gerarchie poco profonde—tipicamente un o due livelli. L’annidamento profondo aumenta il carico cognitivo e rende gli importi onerosi.
Quali sono gli errori più comuni?
Annidamento eccessivo delle directory: Evita strutture come internal/services/user/handlers/http/v1/. Questo crea una complessità di navigazione non necessaria. Invece, usa internal/user/handler.go.
Nomi di pacchetti generici: Nomi come utils, helpers, common o base sono odori di codice. Non comunicano una funzionalità specifica e spesso diventano depositi per codice non correlato. Usa nomi descrittivi: validator, auth, storage, cache.
Dipendenze circolari: Quando il pacchetto A importa il pacchetto B e B importa A, hai una dipendenza circolare—un errore di compilazione in Go. Questo di solito segnala una cattiva separazione delle preoccupazioni. Introduci interfacce o estrai i tipi condivisi in un pacchetto separato.
Mescolare preoccupazioni: Mantieni i gestori HTTP focalizzati su preoccupazioni HTTP, i repository del database su accesso ai dati e la logica aziendale nei pacchetti dei servizi. Posizionare le regole aziendali nei gestori rende difficile il testing e lega la tua logica del dominio a HTTP.
Progettazione Orientata al Dominio e Architettura Esagonale
Per le applicazioni più grandi, specialmente i microservizi, la Progettazione Orientata al Dominio (DDD) con l’Architettura Esagonale fornisce una chiara separazione delle preoccupazioni.
Come strutturo un microservizio seguendo la DDD?
L’Architettura Esagonale organizza il codice in strati concentrici con le dipendenze che scorrono all’interno:
internal/
├── domain/
│ └── user/
│ ├── entity.go # Modelli di dominio e oggetti valore
│ ├── repository.go # Interfaccia del repository (porta)
│ └── service.go # Servizi di dominio
├── application/
│ └── user/
│ ├── create_user.go # Caso d'uso: creare utente
│ ├── get_user.go # Caso d'uso: recuperare utente
│ └── service.go # Orchestrazione del servizio applicativo
├── adapter/
│ ├── http/
│ │ └── user_handler.go # Adattatore HTTP (endpoint REST)
│ ├── postgres/
│ │ └── user_repo.go # Adattatore del database (implementa porta repository)
│ └── redis/
│ └── cache.go # Adattatore del cache
└── api/
└── http/
└── router.go # Configurazione delle route e middleware
Strato di dominio (domain/): Logica aziendale core, entità, oggetti valore e interfacce dei servizi di dominio. Questo strato non ha dipendenze da sistemi esterni—nessun HTTP, nessun import di database. Definisce le interfacce dei repository (porte) che gli adattatori implementano.
Strato applicativo (application/): Caso d’uso che orchestrano gli oggetti del dominio. Ogni caso d’uso (es. “creare utente”, “processare pagamento”) è un file o un pacchetto separato. Questo strato coordina gli oggetti del dominio ma non contiene regole aziendali in sé.
Strato adattatore (adapter/): Implementa le interfacce definite dagli strati interni. I gestori HTTP convertono le richieste in oggetti del dominio, i repository del database implementano la persistenza, le code di messaggi gestiscono la comunicazione asincrona. Questo strato contiene tutto il codice specifico del framework e dell’infrastruttura.
Strato API (api/): Route, middleware, DTO (Data Transfer Objects), versionamento API e specifiche OpenAPI.
Questa struttura assicura che la tua logica aziendale centrale rimanga testabile e indipendente dai framework, dai database o dai servizi esterni. Puoi sostituire PostgreSQL con MongoDB o REST con gRPC senza toccare il codice del dominio.
Pattern pratici per diversi tipi di progetti
Piccolo strumento CLI
Per le applicazioni a riga di comando, vorrai una struttura che supporti diversi comandi e sottocomandi. Considera l’uso di framework come Cobra per la struttura dei comandi e Viper per la gestione della configurazione. La nostra guida su costruire applicazioni CLI in Go con Cobra & Viper copre questo modello in dettaglio.
mytool/
├── main.go
├── command/
│ ├── root.go
│ └── version.go
├── go.mod
└── README.md
Servizio API REST
Quando si costruiscono API REST in Go, questa struttura separa chiaramente le preoccupazioni: i gestori gestiscono le preoccupazioni HTTP, i servizi contengono la logica aziendale e i repository gestiscono l’accesso ai dati. Per una guida completa che copre approcci della libreria standard, framework, autenticazione, pattern di test e best practice per servizi backend scalabili, vedi la nostra guida completa per implementare API REST in Go.
myapi/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ ├── middleware/
│ ├── user/
│ │ ├── handler.go
│ │ ├── service.go
│ │ └── repository.go
│ └── product/
│ ├── handler.go
│ ├── service.go
│ └── repository.go
├── pkg/
│ └── httputil/
├── go.mod
└── README.md
Monorepo con diversi servizi
myproject/
├── cmd/
│ ├── api/
│ ├── worker/
│ └── scheduler/
├── internal/
│ ├── shared/ # Pacchetti interni condivisi
│ ├── api/ # Codice specifico per l'API
│ ├── worker/ # Codice specifico per il worker
│ └── scheduler/ # Codice specifico per il scheduler
├── pkg/ # Librerie condivise
├── go.work # File del workspace Go
└── README.md
Testing e Documentazione
Posiziona i file di test accanto al codice che testano utilizzando il suffisso _test.go:
internal/
└── user/
├── service.go
├── service_test.go
├── repository.go
└── repository_test.go
Questo convenzione mantiene i test vicini all’implementazione, rendendoli facili da trovare e mantenere. Per i test di integrazione che toccano diversi pacchetti, crea una directory separata test/ alla radice del progetto. Per una guida completa su come scrivere test unitari efficaci, inclusi test basati su tabelle, mock, analisi della copertura e best practice, vedi la nostra guida ai test unitari in Go.
La documentazione appartiene a:
README.md: Panoramica del progetto, istruzioni per l’installazione, uso basedocs/: Documentazione dettagliata, decisioni architettoniche, riferimenti APIapi/: Specifiche OpenAPI/Swagger, definizioni protobuf
Per le API REST, generare e servire la documentazione OpenAPI con Swagger è essenziale per la scoperta dell’API e l’esperienza dello sviluppatore. La nostra guida su aggiungere Swagger al tuo API in Go copre l’integrazione con i framework popolari e le best practice.
Gestione delle dipendenze con Go Modules
Ogni progetto Go dovrebbe utilizzare Go Modules per la gestione delle dipendenze. Per un riferimento completo sui comandi Go e la gestione dei moduli, consulta la nostra Go Cheatsheet. Inizializza con:
go mod init github.com/yourusername/myproject
Questo crea go.mod (dipendenze e versioni) e go.sum (checksum per la verifica). Mantieni questi file nel controllo delle versioni per costruzioni riproducibili.
Aggiorna le dipendenze regolarmente:
go get -u ./... # Aggiorna tutte le dipendenze
go mod tidy # Rimuovi dipendenze non utilizzate
go mod verify # Verifica i checksum
Punti chiave
-
Inizia semplice, evolve naturalmente: Non sovra-ingegnerizzare la struttura iniziale. Aggiungi directory e pacchetti quando la complessità lo richiede.
-
Preferisci gerarchie piatte: Limita l’annidamento a un o due livelli. La struttura piatta dei pacchetti di Go migliora la leggibilità.
-
Usa nomi descrittivi per i pacchetti: Evita nomi generici come
utils. Nomina i pacchetti in base a ciò che fanno:auth,storage,validator. -
Separare le preoccupazioni chiaramente: Mantieni i gestori focalizzati su HTTP, i repository su accesso ai dati e la logica aziendale nei pacchetti dei servizi.
-
Usa
internal/per la privacy: Usalo per il codice che non dovrebbe essere importato esternamente. La maggior parte del codice dell’applicazione appartiene qui. -
Applica i pattern architettonici quando necessario: Per sistemi complessi, l’Architettura Esagonale e la DDD forniscono confini chiari e testabilità.
-
Lascia che Go ti guidi: Segui gli idiomi Go invece di importare pattern da altre lingue. Go ha la sua filosofia sulla semplicità e l’organizzazione.
Link utili
- Go Cheatsheet
- Costruire API REST in Go: Guida completa
- Costruire applicazioni CLI in Go con Cobra & Viper
- Test unitari in Go: Struttura & Best Practice
- Aggiungere Swagger al tuo API in Go
- Generics in Go: Caso d’uso e pattern
- Confronto degli ORM Go per PostgreSQL: GORM vs Ent vs Bun vs sqlc
Altri articoli correlati
- Standard Go Project Layout - Linee guida comunitarie per la struttura dei progetti
- Go Modules Reference - Documentazione ufficiale sui moduli Go
- Hexagonal Architecture in Go - Framework di architettura esagonale a livello aziendale
- Domain-Driven Design in Go - Implementazione di DDD a livello produttivo
- Go Project Structure Conventions - Altri pattern e esempi
- Effective Go - Guida ufficiale alle best practice Go