Guida Tecnica

Da Monolite a Microservizi: Guida alla Migrazione Passo per Passo

Guida passo per passo alla scomposizione del monolite: come identificare i confini dei servizi, estrarre in modo incrementale e evitare errori comuni.

Roadmap di migrazione da architettura monolitica a microservizi con passi di estrazione incrementale

In questo articolo:

La scomposizione del monolite è una delle migrazioni più complesse che un team di ingegneria possa intraprendere. È anche una delle più frequentemente mal gestite. I team che tentano di scomporre un monolite spesso sottovalutano le dipendenze dai dati, i confini delle transazioni e la complessità operativa coinvolta. Iniziano con l’intenzione di estrarre microservizi e finiscono con un monolite distribuito: l’accoppiamento originale preservato attraverso i confini di rete, con tutta la complessità dei sistemi distribuiti aggiunta sopra.

Questa guida copre il processo passo per passo per scomporre un monolite in microservizi. Si concentra sulle decisioni che determinano se la migrazione ha successo: come scegliere i confini dei servizi, come sequenziare le estrazioni e come gestire la migrazione dei dati che fa fallire la maggior parte delle scomposizioni dei monoliti.


Dovresti scomporre il tuo monolite?

Prima di iniziare una scomposizione del monolite, verifica che sia la soluzione giusta per i tuoi problemi reali. Non ogni monolite ha bisogno di diventare microservizi.

Un monolite è un problema di deployment quando parti diverse del sistema devono scalare indipendentemente e l’unità di deployment singola del monolite lo impedisce. Un monolite è un problema di autonomia del team quando più team devono lavorare sullo stesso codebase e coordinare i deployment, causando colli di bottiglia. Un monolite è un problema di vincoli tecnologici quando parti diverse del sistema beneficerebbero di scelte tecnologiche diverse.

Se il tuo monolite non ha nessuno di questi problemi, la scomposizione aggiunge complessità operativa senza fornire valore proporzionale. Un monolite ben mantenuto che si distribuisce in modo affidabile e permette ai team di lavorare indipendentemente attraverso buoni confini di modulo è spesso la scelta giusta. La guida rilevante è nella sezione legacy modernization.


Identificare i confini dei servizi

La ragione più comune per cui le migrazioni di monoliti falliscono è la scarsa definizione dei confini dei servizi. I servizi definiti per livelli tecnici piuttosto che per capacità aziendali ereditano l’accoppiamento del monolite originale.

I confini dei servizi devono allinearsi con i domini aziendali. Il Domain-Driven Design (DDD) fornisce il vocabolario: un bounded context è un confine all’interno del quale si applica un modello di dominio specifico. Il servizio che gestisce l’autenticazione utente opera con un modello di dominio; il servizio che gestisce l’elaborazione degli ordini opera con un altro.

Segnali pratici per i candidati ai confini dei servizi:

Diversi tassi di cambiamento. Funzionalità che cambiano frequentemente e funzionalità che cambiano raramente dovrebbero trovarsi in servizi diversi. Accoppiarle significa che ogni cambiamento alla logica dei prezzi richiede il test e il deployment del codice dei template di notifica.

Diversi requisiti di scaling. Funzionalità che devono gestire carichi variabili dovrebbero trovarsi in un servizio che può scalare indipendentemente.

Proprietà diversa dei team. Se il team di fatturazione e il team di logistica lavorano nello stesso modulo del codebase, quel modulo è un candidato per l’estrazione.

Modelli di dati indipendenti. Un confine di servizio è pulito quando il servizio può possedere i propri dati senza condividere una tabella di database con un altro servizio.


Il processo di estrazione passo per passo

Usa lo strangler fig pattern come strategia di migrazione. Questo significa installare un proxy o API gateway davanti al monolite, poi instradare le capacità una alla volta dal monolite ai servizi estratti.

Passo 1: Mappa il monolite. Documenta le principali aree di capacità, le loro dipendenze, la loro frequenza di cambiamento e i loro attuali proprietari.

Passo 2: Scegli il primo obiettivo di estrazione. Seleziona la capacità più indipendente dal resto del monolite, con un’interfaccia ben definita. Un servizio di notifiche è una prima estrazione classica: ha input chiari (eventi da altre parti del sistema), output chiari (email, SMS) e stato condiviso minimo.

Passo 3: Installa l’infrastruttura di routing. Distribuisci un API gateway o proxy davanti al monolite. Tutto il traffico va ancora al monolite. Questo stabilisce l’infrastruttura di routing senza alcun cambiamento funzionale.

Passo 4: Estrai e instrada. Costruisci il primo servizio. Quando è pronto per la produzione, instrada il traffico rilevante attraverso il gateway al nuovo servizio. Mantieni la capacità nel monolite temporaneamente come fallback. Monitora per 48-72 ore, poi rimuovi il gestore del monolite.

Passo 5: Estrai i dati. Una volta che il servizio gestisce il proprio traffico, ha bisogno del proprio data store.

Passo 6: Ripeti. Continua con il prossimo obiettivo di estrazione.


Migrazione dei dati e confini delle transazioni

Il coupling del database è la ragione più comune per cui le scomposizioni dei monoliti si bloccano. Il database del monolite è un unico store condiviso con foreign key e join attraverso quelli che dovrebbero essere confini di servizi.

Il pattern expand-contract è l’approccio standard alla migrazione dei dati senza downtime. Nella fase di espansione, il servizio scrive sia nel database del monolite che nel proprio. Nella fase di contrazione, una volta verificato che le letture dal nuovo store sono corrette, il servizio smette di leggere dal database del monolite.

I confini delle transazioni sono il secondo problema difficile. Se la creazione di un ordine nel monolite coinvolge la scrittura nella tabella degli ordini, nella tabella dell’inventario e nella tabella dell’account cliente in una singola transazione, l’estrazione dell’inventario in un servizio separato rompe il confine della transazione. Le opzioni includono: usare pattern saga (transazioni compensative tra servizi), accettare la consistenza eventuale con una corretta gestione degli errori, o ridisegnare il confine.

Non esiste una risposta universalmente corretta. La scelta dipende dai requisiti aziendali per la consistenza. Le transazioni finanziarie di solito richiedono forte consistenza. I workflow guidati da eventi di solito tollerano la consistenza eventuale.


Conclusione

La scomposizione del monolite ha successo quando i confini dei servizi si allineano con i domini aziendali, l’estrazione è incrementale usando lo strangler fig pattern e la migrazione dei dati è pianificata esplicitamente piuttosto che differita. I team che falliscono fanno il contrario: estraggono per livello tecnico, migrano grandi blocchi simultaneamente e assumono che la migrazione del database si risolva da sola.

La ricompensa per farlo correttamente: i team che in precedenza distribuivano una volta a settimana diventano indipendenti, la frequenza di deployment aumenta da 4/mese a 40/mese e l’isolamento degli incidenti diventa banale perché un fallimento in un servizio non si propaga all’intero sistema.

Hai un codebase con questi problemi? Parliamo del tuo sistema