I microservizi sono uno degli argomenti più discussi nello sviluppo backend moderno. Se hai già lavorato con Spring Boot, probabilmente ti sei imbattuto in termini come API Gateway, Service Discovery, Config Server, Docker, Kubernetes, RabbitMQ o Kafka.
La domanda che molti sviluppatori si pongono è sempre la stessa:
Servono davvero tutti questi componenti?
Oppure:
Perché dovrei complicare un’applicazione che con Spring Boot funziona già benissimo?
In questo articolo cercheremo di rispondere proprio a queste domande. Non ci limiteremo alla teoria: utilizzeremo esempi concreti e semplici metafore per capire perché le architetture a microservizi sono nate e quando hanno davvero senso.
Il limite delle applicazioni monolitiche
Immagina di costruire una casa composta da un’unica enorme stanza.
All’interno trovi:
-
- cucina;
-
- bagno;
-
- soggiorno;
-
- camera da letto;
-
- garage.
Finché tutto funziona, non ci sono problemi.
Ma cosa succede quando vuoi ristrutturare il bagno?
Devi chiudere tutta la casa.
Vuoi ampliare la cucina?
Rischi di dover modificare anche il soggiorno.
Questa è una buona rappresentazione di un’applicazione monolitica.
Tutto vive nello stesso progetto:
-
- un unico deploy;
-
- un unico processo;
-
- un unico artefatto;
-
- spesso un unico database.
Per molti anni questo modello è stato (e continua a essere) perfettamente valido.
Anzi, è importante chiarire un punto: un monolite non è una cattiva architettura. Per applicazioni piccole o di media complessità è spesso la soluzione migliore, perché è più semplice da sviluppare, distribuire e mantenere.
I problemi iniziano quando il progetto cresce.
Pensiamo a un e-commerce.
All’inizio contiene pochi moduli:
-
- autenticazione;
-
- utenti;
-
- catalogo prodotti;
-
- carrello;
-
- ordini;
-
- pagamenti;
-
- spedizioni;
-
- notifiche.
Con il passare degli anni arrivano nuovi sviluppatori, nuove funzionalità, integrazioni esterne e clienti sempre più numerosi.
Il progetto diventa enorme.
Le compilazioni richiedono sempre più tempo.
Ogni deploy coinvolge l’intera applicazione.
Una piccola modifica può avere effetti imprevedibili su altre parti del sistema.
È come avere un enorme gomitolo di fili: tiri un filo e non sai quale lampadina si spegnerà.
L’idea alla base dei microservizi
Torniamo alla metafora della casa.
Questa volta, invece di costruire un’unica grande abitazione, realizziamo tanti edifici indipendenti.
Una casetta ospita la cucina.
Una il bagno.
Una il garage.
Una la camera da letto.
Se ristrutturi il bagno, la cucina continua a funzionare senza alcun problema.
Questa è l’idea dei microservizi.
Ogni servizio si occupa di una sola responsabilità.
Ad esempio:
-
- User Service gestisce gli utenti;
-
- Order Service gestisce gli ordini;
-
- Payment Service gestisce esclusivamente i pagamenti.
Ogni microservizio è una normale applicazione Spring Boot con:
-
- il proprio codice;
-
- il proprio ciclo di vita;
-
- il proprio deploy;
-
- le proprie API REST;
-
- la propria responsabilità.
È importante sottolineare un aspetto spesso frainteso.
Microservizio non significa progetto piccolo.
Può contenere migliaia di classi.
Ciò che conta è che abbia una responsabilità ben definita e indipendente dagli altri servizi.
Perché Spring Boot è così diffuso nei microservizi
Spring Boot si presta perfettamente a questo tipo di architettura.
Ogni microservizio è semplicemente una normale applicazione Spring Boot che espone API REST e può essere sviluppata, testata e distribuita in modo indipendente.
Non esiste un “Spring Boot per i microservizi”: gli strumenti sono gli stessi che già utilizziamo nelle applicazioni tradizionali.
Ciò che cambia è il modo in cui le varie applicazioni collaborano tra loro.
Il primo problema: come comunicano i servizi?
Immaginiamo che Order Service debba recuperare i dati di un utente.
La soluzione più semplice è effettuare una chiamata HTTP verso User Service.
È come fare una telefonata.
“Ciao User Service, puoi inviarmi i dati dell’utente numero 25?”
Il servizio risponde e la comunicazione termina.
Sembra semplice, ma emergono subito nuove domande.
-
- E se il servizio è spento?
-
- E se cambia indirizzo?
-
- E se esistono più copie dello stesso servizio?
Per risolvere questi problemi entra in gioco l’ecosistema Spring Cloud e, più in generale, tutti gli strumenti che supportano le architetture distribuite.
Service Discovery
Immagina una città in cui ogni negozio cambia indirizzo continuamente.
Ogni mattina dovresti cercarlo su Google Maps.
Sarebbe impossibile.
Serve un elenco sempre aggiornato.
Nei microservizi questo ruolo è svolto dal Service Discovery.
Ogni servizio, quando viene avviato, comunica:
“Sono User Service e puoi trovarmi a questo indirizzo.”
Quando un altro servizio deve contattarlo, chiede semplicemente:
“Dove si trova User Service?”
Il Service Discovery restituisce l’indirizzo corretto.
In passato, nell’ecosistema Spring Cloud, il componente più utilizzato era Netflix Eureka. Oggi, soprattutto in ambienti Kubernetes o cloud-native, il Service Discovery viene spesso fornito direttamente dalla piattaforma, senza dover installare componenti dedicati.
API Gateway
Molto spesso API Gateway e Service Discovery vengono confusi, ma svolgono compiti completamente diversi.
Immagina un enorme centro commerciale con centinaia di negozi.
Appena entri trovi una reception.
Tu dici:
“Vorrei andare da Apple.”
La receptionist controlla dove si trova il negozio e ti indica il percorso corretto.
Oppure chiedi di effettuare un reso.
La receptionist verifica i tuoi documenti e ti indirizza allo sportello appropriato.
L’API Gateway svolge proprio questo ruolo.
È l’unico punto di ingresso dell’applicazione.
Può:
-
- autenticare gli utenti;
-
- verificare i token JWT;
-
- applicare limiti sul numero di richieste;
-
- registrare gli accessi;
-
- inoltrare ogni richiesta al microservizio corretto.
Il Service Discovery, invece, risponde soltanto a una domanda:
“Dove si trova questo servizio in questo momento?”
Sono due componenti differenti che spesso collaborano tra loro.
Load Balancer
Supponiamo che esista una sola istanza di Order Service.
Finché gli utenti sono pochi, tutto funziona.
Ma se gli accessi aumentano, una sola istanza potrebbe non essere sufficiente.
Decidiamo quindi di avviarne tre.
Nasce però un nuovo problema.
A quale delle tre copie deve essere inviata ogni richiesta?
Qui entra in gioco il Load Balancer, che distribuisce il traffico tra le varie istanze disponibili.
Per poter scegliere, il Load Balancer deve sapere quali istanze esistono, motivo per cui lavora spesso insieme al Service Discovery.
In molti casi moderni, soprattutto con Kubernetes, queste funzionalità sono integrate direttamente nella piattaforma.
Config Server
Immagina di avere cento microservizi.
Ognuno contiene configurazioni come:
-
- password del database;
-
- URL dei servizi esterni;
-
- chiavi API;
-
- porte di ascolto.
Se una password cambia, dovresti modificare cento applicazioni.
Il Config Server centralizza tutte queste configurazioni, permettendo ai servizi di leggerle da un unico punto.
È come avere un archivio condiviso da cui ogni applicazione recupera automaticamente le proprie impostazioni.
Comunicazione asincrona: RabbitMQ e Kafka
Finora abbiamo visto comunicazioni sincrone tramite HTTP.
Una chiamata HTTP è simile a una telefonata: chi chiama resta in attesa della risposta.
Esiste però un altro modello.
Immagina di spedire una lettera.
La imbuchi nella cassetta postale e continui il tuo lavoro.
Il destinatario la leggerà quando sarà disponibile.
Questo è il principio della messaggistica asincrona.
Strumenti come RabbitMQ e Kafka permettono ai servizi di scambiarsi eventi senza dover attendere immediatamente una risposta.
Questo rende il sistema più scalabile e meno dipendente dalla disponibilità immediata degli altri servizi.
Ogni microservizio possiede i propri dati
Uno dei principi fondamentali dei microservizi riguarda la gestione dei dati.
Più che avere necessariamente un database separato, ogni microservizio deve essere l’unico proprietario dei propri dati.
Nella maggior parte dei casi questo porta ad avere un database dedicato (o almeno uno schema dedicato), ma il principio realmente importante è un altro:
nessun servizio dovrebbe modificare direttamente le tabelle appartenenti a un altro servizio.
In caso contrario si perderebbe gran parte dell’indipendenza che rende vantaggiosa un’architettura a microservizi.
Docker
Quando il numero dei servizi aumenta, installarli manualmente diventa complicato.
Docker risolve questo problema.
Possiamo immaginare ogni container come una scatola che contiene tutto ciò che serve per eseguire l’applicazione:
-
- Java;
-
- Spring Boot;
-
- librerie;
-
- configurazione;
-
- dipendenze.
Ogni microservizio viene distribuito nel proprio container, garantendo che funzioni nello stesso modo su qualsiasi macchina.
Kubernetes
Docker risolve il problema dell’esecuzione dei container.
Ma chi gestisce centinaia di container?
Qui entra in gioco Kubernetes.
Possiamo immaginarlo come il direttore d’orchestra dell’intera infrastruttura.
Se un container va in crash, Kubernetes lo ricrea automaticamente.
Se arriva un picco di traffico, crea nuove copie del servizio.
Quando il traffico diminuisce, elimina le istanze non più necessarie.
Se un server fisico si guasta, sposta automaticamente i container su un altro nodo.
Infine, permette di aggiornare le applicazioni senza interrompere il servizio grazie ai Rolling Update, sostituendo progressivamente le vecchie versioni con quelle nuove.
Oltre a questo, Kubernetes integra funzionalità che un tempo richiedevano componenti dedicati, come il Service Discovery e il Load Balancing.
Conclusioni
I microservizi non sono una moda e nemmeno la soluzione universale.
Nascono per risolvere problemi che emergono quando un’applicazione cresce molto in termini di dimensioni, numero di sviluppatori o carico di lavoro.
Per un piccolo progetto, un monolite ben progettato rimane spesso la scelta migliore.
Quando invece il sistema diventa complesso, strumenti come Spring Boot, Docker, Kubernetes, API Gateway e i sistemi di messaggistica permettono di costruire applicazioni più modulari, scalabili e semplici da evolvere nel tempo.
La cosa importante è ricordare che i microservizi non sono semplicemente “tanti piccoli progetti”, ma un modo diverso di progettare il software, mettendo al centro l’indipendenza dei servizi e la separazione delle responsabilità.
