Quando si parla di migrazione da un monolite a una microservices architecture, una delle domande più difficili è:
Come facciamo a modernizzare un sistema legacy senza dover riscrivere tutto da zero?
Una delle risposte più interessanti è lo Strangler Pattern.
Il nome deriva dall’idea di una pianta rampicante che cresce attorno a un albero fino a sostituirlo progressivamente.
Ed è esattamente ciò che succede con il software:
il nuovo sistema cresce attorno al vecchio, sostituendolo gradualmente.
Come funziona lo Strangler Pattern?
L’idea è evitare una migrazione del tipo:
MONOLITE
|
| rewrite completo
↓
MICROSERVICES
Questo approccio, spesso chiamato Big Bang Rewrite, è estremamente rischioso.
Con lo Strangler Pattern facciamo invece:
┌──────────────┐
Request ───────────>│ API Gateway │
└──────┬───────┘
│
┌──────────┴──────────┐
↓ ↓
Legacy Monolith New Microservice
Inizialmente quasi tutto il traffico continua ad andare al monolite.
Poi iniziamo a estrarre alcune funzionalità.
Step 1: identificare una funzionalità
Supponiamo di avere un monolite Java:
┌───────┐
MONOLITH
- Users
- Orders
- Payments
- Inventory
- Notifications
└───────┘
Decidiamo, per esempio, di estrarre Orders.
Creiamo:
Order Service
che diventerà il nuovo microservizio responsabile degli ordini.
Step 2: creare il nuovo microservizio
Possiamo implementarlo con Spring Boot:
@RestController
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/{id}")
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
}
A questo punto abbiamo due implementazioni:
Orders
|
┌───────┴────────┐
↓ ↓
Monolith Order Service
Il problema diventa:
Chi deve ricevere le richieste?
Ed è qui che entra in gioco il routing.
Step 3: introdurre un API Gateway
Possiamo utilizzare, ad esempio, Spring Cloud Gateway.
Il gateway diventa il punto di ingresso:
Client
|
v
┌─────────────┐
│ API Gateway │
└──────┬──────┘
|
┌────────┴────────┐
↓ ↓
Monolith Order Service
Una configurazione semplificata potrebbe essere:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("legacy", r -> r.path("/old-api/**")
.uri("http://monolith-system"))
.route("new", r -> r.path("/new-api/**")
.uri("http://new-microservice"))
.build();
}
Il gateway decide quindi dove mandare la richiesta.
Step 4: spostare progressivamente il traffico
All’inizio potremmo avere:
90% → Monolith
10% → Microservice
Poi:
70% → Monolith
30% → Microservice
E successivamente:
30% → Monolith
70% → Microservice
Fino ad arrivare a:
0% → Monolith
100% → Microservice
A quel punto quella particolare funzionalità è stata completamente migrata.
Possiamo quindi rimuoverla dal monolite.
Lo Strangler Pattern non significa necessariamente “microservices”
Questo è un punto importante.
Lo Strangler Pattern è prima di tutto una strategia di modernizzazione incrementale.
Il risultato potrebbe essere:
Monolith
+
New Service
+
New Service
oppure:
Monolith
+
Microservices
+
Serverless
+
External APIs
Il pattern descrive soprattutto come effettuare la transizione, non necessariamente quale debba essere l’architettura finale.
Un esempio più realistico
Immaginiamo un e-commerce legacy:
MONOLITH
┌─────────────────────────────────────┐
│ │
│ Users Orders Payments │
│ │
│ Inventory Shipping Notifications│
│ │
└─────────────────────────────────────┘
Potremmo procedere così.
Fase 1
Estraggo Notifications:
Gateway
|
┌───────────┴───────────┐
↓ ↓
Monolith Notification Service
Fase 2
Estraggo Orders:
Gateway
|
┌────────────┼────────────┐
↓ ↓ ↓
Monolith Orders Notifications
Fase 3
Estraggo Payments:
Gateway
|
┌───────────────┼────────────────┐
↓ ↓ ↓
Monolith Orders Payments
|
Notifications
Il monolite diventa progressivamente più piccolo.
Alla fine:
Gateway
|
┌──────────────┼──────────────┐
↓ ↓ ↓
Users Orders Payments
|
Notifications
E il monolite può finalmente essere eliminato.
Il vero vantaggio: ridurre il rischio
Il principale vantaggio dello Strangler Pattern non è semplicemente “avere microservizi”.
È ridurre il rischio della migrazione.
Una riscrittura completa può durare mesi o anni.
E durante quel periodo abbiamo due problemi:
Vecchio sistema
+
Nuovo sistema
senza sapere se il nuovo sistema funzionerà davvero.
Con lo Strangler Pattern possiamo invece fare piccoli passi:
Extract
↓
Test
↓
Deploy
↓
Monitor
↓
Shift traffic
↓
Remove legacy code
Ogni migrazione diventa quindi un cambiamento relativamente piccolo e osservabile.
Un altro vantaggio: rollback
Immaginiamo che il nuovo Order Service abbia un problema.
Con un’architettura basata sul routing possiamo potenzialmente riportare il traffico al monolite:
Gateway
|
┌────┴────┐
↓ ↓
Monolith Service
↑
|
rollback
Questo è molto più semplice rispetto a un Big Bang Rewrite nel quale il vecchio sistema è stato completamente eliminato.
Ma attenzione alla complessità
Lo Strangler Pattern non è gratuito.
Durante la migrazione dobbiamo gestire:
Monolith
+
Microservices
+
Gateway
+
Distributed Data
+
Observability
+
Deployment
Per un certo periodo il sistema può diventare più complesso, non meno.
In particolare, uno dei problemi più difficili è spesso il database.
Potremmo infatti avere:
Monolith
|
v
Shared Database
↑
|
Order Service
e questo crea un forte accoppiamento.
Una migrazione realmente completa richiede quindi spesso anche una progressiva separazione dei dati:
Monolith → Legacy DB
Order Service → Order DB
Questa parte può essere molto più difficile del semplice spostamento delle API.
Strangler Pattern vs Big Bang
Possiamo riassumere la differenza così:
BIG BANG
Monolith
|
| rewrite
↓
Microservices
contro:
STRANGLER
Monolith
|
+----> New Service
|
+----> New Service
|
+----> New Service
|
↓
Monolith sempre più piccolo
|
↓
Monolith eliminato
Il secondo approccio non elimina il rischio.
Lo distribuisce nel tempo e permette di verificare continuamente che ogni passo funzioni.
La lezione architetturale
Lo Strangler Pattern ci ricorda una cosa importante:
modernizzare un sistema legacy non significa necessariamente riscriverlo.
Spesso è più efficace costruire il nuovo sistema accanto al vecchio e spostare progressivamente le responsabilità.
Il pattern diventa quindi particolarmente interessante quando abbiamo:
- sistemi legacy molto grandi;
- business che non può permettersi lunghi downtime;
- applicazioni difficili da riscrivere;
- necessità di modernizzazione graduale;
- team che devono continuare a sviluppare nuove funzionalità durante la migrazione.
In definitiva, lo Strangler Pattern non è soltanto un pattern tecnico.
È una strategia di gestione del rischio architetturale:
Legacy
↓
Extract
↓
Route
↓
Observe
↓
Migrate
↓
Remove
Un passo alla volta.
Ed è proprio questo che rende possibile trasformare un monolite in una nuova architettura senza dover fare un salto nel vuoto.
#softwarearchitecture #microservices #java #springboot #designpatterns #legacy #cloud #softwareengineering

