Struttura del Progetto Go: Pratiche e Pattern
Struttura i tuoi progetti Go per scalabilità e chiarezza
Strutturare efficacemente un progetto Go è fondamentale per la manutenibilità a lungo termine, la collaborazione del team e la scalabilità. A differenza dei framework che impongono layout di directory rigidi, Go abbraccia la flessibilità, ma con questa libertà arriva la responsabilità di scegliere schemi che servano le esigenze specifiche del tuo progetto.

Comprendere la filosofia di Go sulla struttura dei progetti
La filosofia di design minimalista di Go si estende all’organizzazione del progetto. Il linguaggio non impone una struttura specifica, facendo invece affidamento sui sviluppatori per prendere decisioni informate. Questo approccio ha portato la comunità a sviluppare diversi schemi collaudati, dai layout piatti semplici per piccoli progetti ad architetture sofisticate per sistemi enterprise.
Il principio chiave è semplicità prima, complessità solo se necessaria. Molti sviluppatori cadono nella trappola del sovra-ingegnerismo nella struttura iniziale, creando directory profondamente annidate e astrazioni premature. Inizia con ciò di cui hai bisogno oggi e rifattorizza man mano che il tuo progetto cresce.
Quando dovrei usare la directory internal/ rispetto a pkg/?
La directory internal/ serve 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/ perfetta per la logica dell’applicazione privata, le regole di business e le utilità non destinate al riutilizzo al di fuori del tuo progetto.
La directory pkg/, d’altra parte, segnala che il codice è destinato al consumo esterno. Posiziona il codice qui solo se stai costruendo una libreria o componenti riutilizzabili che vuoi che altri importino. Molti progetti non hanno bisogno di pkg/ affatto: se il tuo codice non viene consumato esternamente, tenerlo nella root o in internal/ è più pulito. Quando si creano librerie riutilizzabili, considera l’uso di Go generics per creare componenti riutilizzabili e tipizzati in modo sicuro.
Il layout standard del progetto Go
Lo schema più ampiamente riconosciuto è il golang-standards/project-layout, anche se è importante notare che non si tratta di uno standard ufficiale. Ecco a cosa assomiglia 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
Molti team mantengono un piccolo pacchetto di logging condiviso sotto internal/ (ad esempio internal/logx) in modo che i binari in cmd/ configurino un logger all’avvio; pkg/logger/ nello schema sopra è appropriato solo quando quel codice è destinato a essere importato da altri moduli. Per una configurazione log/slog orientata alla produzione (righe JSON, redaction, campi di contesto e trace, e segnali che funzionano bene con stack di monitoraggio), consulta Structured Logging in Go with slog for Observability and Alerting.
La directory cmd/
La directory cmd/ contiene i punti di ingresso della tua applicazione. Ogni sottodirectory rappresenta un binario eseguibile separato. Ad esempio, cmd/api/main.go costruisce il tuo server API, mentre cmd/worker/main.go potrebbe costruire un processore di job in background.
Best practice: Mantieni i tuoi file main.go minimi—solo sufficienti per configurare le dipendenze, caricare la configurazione e avviare l’applicazione. Tutta la logica sostanziale appartiene ai pacchetti che main.go importa.
// 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/
Qui risiede il codice privato della tua applicazione. Il compilatore Go impedisce a qualsiasi progetto esterno di importare pacchetti all’interno di internal/, rendendolo ideale per:
- Logica di business e modelli di dominio
- Servizi dell’applicazione
- API e interfacce interne
- Repository del database (per scegliere l’ORM giusto, consulta il nostro confronto di Go ORMs per PostgreSQL)
- Logica di autenticazione e autorizzazione
Organizza internal/ per funzionalità o dominio, non per strato tecnico. Invece di internal/handlers/, internal/services/, internal/repositories/, preferisci internal/user/, internal/order/, internal/payment/ dove ogni pacchetto contiene i suoi 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, introduci le directory. Potresti aggiungere un pacchetto db/ quando la logica del database diventa sostanziale, o un pacchetto api/ quando gli handler HTTP moltiplicano. Lascia che la struttura emerga naturalmente piuttosto che imporla in anticipo.
Strutture Piatte vs. Annidate: Trovare l’Equilibrio
Uno degli errori più comuni nella struttura dei progetti Go è l’eccessivo annidamento. Go favorisce gerarchie poco profonde—tipicamente uno o due livelli. L’annidamento profondo aumenta il carico cognitivo e rende gli import ingombranti.
Quali Sono gli Errori Più Comuni?
Annidamento eccessivo delle directory: Evita strutture come internal/services/user/handlers/http/v1/. Questo crea complessità di navigazione inutile. Invece, usa internal/user/handler.go.
Nomi di pacchetto generici: Nomi come utils, helpers, common o base sono code smell. Non trasmettono funzionalità specifiche e spesso diventano discariche 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 segnala tipicamente una scarsa separazione delle preoccupazioni. Introduci interfacce o estrai tipi condivisi in un pacchetto separato.
Mescolare le preoccupazioni: Mantieni gli handler HTTP focalizzati sulle preoccupazioni HTTP, i repository del database sull’accesso ai dati e la logica di business nei pacchetti dei servizi. Posizionare le regole di business negli handler rende difficile il testing e accoppia la logica di dominio a HTTP.
Domain-Driven Design e Architettura Esagonale
Per applicazioni più grandi, specialmente microservizi, il Domain-Driven Design (DDD) con l’Architettura Esagonale fornisce una chiara separazione delle preoccupazioni.
Come Strutturo un Microservizio Seguendo il Domain-Driven Design?
L’Architettura Esagonale organizza il codice in strati concentrici con dipendenze che fluiscono verso l’interno:
internal/
├── domain/
│ └── user/
│ ├── entity.go # Modelli di dominio e oggetti valore
│ ├── repository.go # Interfaccia del repository (port)
│ └── service.go # Servizi di dominio
├── application/
│ └── user/
│ ├── create_user.go # Use case: crea utente
│ ├── get_user.go # Use case: recupera utente
│ └── service.go # Orchestrazione del servizio di applicazione
├── adapter/
│ ├── http/
│ │ └── user_handler.go # Adattatore HTTP (endpoint REST)
│ ├── postgres/
│ │ └── user_repo.go # Adattatore del database (implementa il port del repository)
│ └── redis/
│ └── cache.go # Adattatore della cache
└── api/
└── http/
└── router.go # Configurazione delle rotte e middleware
Strato di dominio (domain/): Logica di business centrale, entità, oggetti valore e interfacce dei servizi di dominio. Questo strato non ha dipendenze da sistemi esterni—nessun HTTP, nessun import del database. Definisce interfacce di repository (port) che gli adattatori implementano.
Strato di applicazione (application/): Use case che orchestrano oggetti di dominio. Ogni use case (es. “crea utente”, “processa pagamento”) è un file o pacchetto separato. Questo strato coordina gli oggetti di dominio ma non contiene regole di business di per sé.
Strato di adattatore (adapter/): Implementa interfacce definite dagli strati interni. Gli handler HTTP convertono le richieste in oggetti di 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/): Rotte, middleware, DTO (Data Transfer Objects), versioning API e specifiche OpenAPI.
Questa struttura garantisce che la tua logica di business centrale rimanga testabile e indipendente da framework, database o servizi esterni. Puoi sostituire PostgreSQL con MongoDB, o REST con gRPC, senza toccare il codice di dominio. Se adotti CQRS all’interno di questo layout, Implementing CQRS in Go mostra come lo strato application/ si mappa naturalmente su pacchetti separati per handler di comandi e query, mantenendo il lato dei comandi rigoroso e il lato delle query orientato agli DTO.
Schemi Pratici per Diversi Tipi di Progetto
Piccolo Strumento CLI
Per le applicazioni da riga di comando, vorrai una struttura che supporti più 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 building CLI applications in Go with Cobra & Viper copre questo schema in dettaglio.
mytool/
├── main.go
├── command/
│ ├── root.go
│ └── version.go
├── go.mod
└── README.md
Servizio REST API
Quando si costruiscono REST API in Go, questa struttura separa chiaramente le preoccupazioni: gli handler gestiscono le preoccupazioni HTTP, i servizi contengono la logica di business e i repository gestiscono l’accesso ai dati. Per una guida completa che copre approcci della libreria standard, framework, autenticazione, schemi di test e best practice pronte per la produzione, consulta la nostra complete guide to building REST APIs in Go. Per il logging strutturato JSON, la correlazione di richieste e trace, e forme di log che supportano l’alerting, consulta Structured Logging in Go with slog for Observability and Alerting.
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 Più Servizi
myproject/
├── cmd/
│ ├── api/
│ ├── worker/
│ └── scheduler/
├── internal/
│ ├── shared/ # Pacchetti interni condivisi
│ ├── api/ # Codice specifico per API
│ ├── worker/ # Codice specifico per Worker
│ └── scheduler/ # Codice specifico per Scheduler
├── pkg/ # Librerie condivise
├── go.work # File di workspace Go
└── README.md
Testing e Documentazione
Posiziona i file di test accanto al codice che testano usando il suffisso _test.go:
internal/
└── user/
├── service.go
├── service_test.go
├── repository.go
└── repository_test.go
Questa convenzione mantiene i test vicini all’implementazione, rendendoli facili da trovare e mantenere. Per i test di integrazione che toccano più pacchetti, crea una directory test/ separata alla root del progetto. Per una guida completa sulla scrittura di test unitari efficaci, inclusi test guidati da tabella, mock, analisi della copertura e best practice, consulta la nostra guide to Go unit testing structure and best practices.
La documentazione appartiene a:
README.md: Panoramica del progetto, istruzioni di setup, utilizzo di basedocs/: Documentazione dettagliata, decisioni architetturali, riferimenti APIapi/: Specifiche OpenAPI/Swagger, definizioni protobuf
Per le REST API, la generazione e il servizio della documentazione OpenAPI con Swagger sono essenziali per la scopribilità dell’API e l’esperienza dello sviluppatore. La nostra guida su adding Swagger to your Go API copre l’integrazione con framework popolari e le best practice.
Gestione delle Dipendenze con Go Modules
Ogni progetto Go dovrebbe usare Go Modules per la gestione delle dipendenze. Per un riferimento completo sui comandi Go e la gestione dei moduli, controlla il nostro 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 build riproducibili.
Aggiorna regolarmente le dipendenze:
go get -u ./... # Aggiorna tutte le dipendenze
go mod tidy # Rimuovi dipendenze inutilizzate
go mod verify # Verifica i checksum
Punti Chiave
-
Inizia semplice, evolvi naturalmente: Non sovra-ingegnerizzare la tua struttura iniziale. Aggiungi directory e pacchetti quando la complessità lo richiede.
-
Preferisci gerarchie piatte: Limita l’annidamento a uno o due livelli. La struttura piatta dei pacchetti Go migliora la leggibilità.
-
Usa nomi di pacchetto descrittivi: Evita nomi generici come
utils. Nomina i pacchetti in base a ciò che fanno:auth,storage,validator. -
Separa chiaramente le preoccupazioni: Mantieni gli handler focalizzati su HTTP, i repository sull’accesso ai dati e la logica di business nei pacchetti dei servizi.
-
Sfrutta
internal/per la privacy: Usalo per codice che non dovrebbe essere importato esternamente. La maggior parte del codice dell’applicazione appartiene qui. -
Applica schemi architetturali quando necessario: Per sistemi complessi, l’Architettura Esagonale e il DDD forniscono confini chiari e testabilità.
-
Lasciati guidare da Go: Segui gli idiomi Go piuttosto che importare schemi da altri linguaggi. Go ha la sua filosofia sulla semplicità e l’organizzazione.
Link Utili
- Go Cheatsheet
- Building REST APIs in Go: Complete Guide
- Building CLI Applications in Go with Cobra & Viper
- Go Unit Testing: Structure & Best Practices
- Structured Logging in Go with slog for Observability and Alerting
- Adding Swagger to Your Go API
- Go Generics: Use Cases and Patterns
- Comparing Go ORMs for PostgreSQL: GORM vs Ent vs Bun vs sqlc
Altri Articoli Correlati
- Standard Go Project Layout - Linee guida per la struttura del progetto guidate dalla comunità
- Go Modules Reference - Documentazione ufficiale dei moduli Go
- Hexagonal Architecture in Go - Framework di architettura esagonale di livello enterprise
- Domain-Driven Design in Go - Implementazione DDD pronta per la produzione
- Go Project Structure Conventions - Schemi ed esempi aggiuntivi
- Effective Go - Guida ufficiale alle best practice di Go
- App Architecture hub — API design, code structure, and integration patterns