Toute équipe d'ingénierie finit par faire face à la même réalité effrayante : son monolithe hérité devient un goulot d'étranglement pour l'innovation. Le code est emmêlé, les cycles de déploiement sont lents et ajouter de nouvelles fonctionnalités ressemble à désamorcer une bombe. Pendant des années, le conseil standard était de refondre « en un seul bloc » en construisant un nouveau système à partir de zéro et en basculant l'interrupteur. Cependant, cette approche comporte un risque immense et échoue souvent en raison de dépassements de coûts et de retards de calendrier.
Voici le Pattern Strangler Fig, une stratégie de migration popularisée par Martin Fowler. Tout comme le figuier strangler pousse autour d'un arbre hôte, l'absorbant progressivement jusqu'à ce que l'hôte meure, ce pattern consiste à remplacer incrémentalement des parties d'un système hérité par de nouveaux microservices. Aujourd'hui, nous explorerons comment mettre en œuvre efficacement ce pattern, gérer le routage du trafic et garantir zéro interruption de service pendant la transition.
Pourquoi la migration incrémentale est importante
Le principal avantage du Pattern Strangler Fig est l'atténuation des risques. En migrant les fonctionnalités morceau par morceau, vous pouvez valider chaque nouveau service indépendamment. Si un nouveau service échoue, vous pouvez simplement rediriger le trafic vers le système hérité sans impacter le reste de l'application. Cette approche itérative permet aux équipes de livrer de la valeur en continu plutôt que d'attendre une date de « complétion » hypothétique dans plusieurs années.
Composants clés de la stratégie
Pour mettre en œuvre avec succès le pattern, vous avez besoin d'une approche stratégique pour le routage du trafic. Le mécanisme le plus courant est une passerelle API (API Gateway) ou une couche de proxy située devant à la fois le monolithe hérité et les nouveaux microservices. Cette passerelle agit comme un agent de circulation, décidant si une requête doit être traitée par l'ancien système ou par le nouveau service modernisé.
Examinons une mise en œuvre pratique à l'aide d'un exemple Node.js/Express. Dans ce scénario, nous extrayons le service d'authentification des utilisateurs d'un monolithe plus large.
Mise en œuvre de la logique de la passerelle
La passerelle doit disposer d'un mécanisme pour router les requêtes en fonction des chemins d'URL ou de critères spécifiques. Voici un exemple simplifié de la façon dont cette logique de routage pourrait apparaître dans une fonction middleware.
const express = require('express');
const app = express();
// Configuration pour les indicateurs de fonctionnalité ou les règles de routage
const STRANGLER_CONFIG = {
'/api/users': 'new_microservice',
'/api/orders': 'legacy_monolith' // Toujours dans le monolithe
};
// Middleware pour intercepter les requêtes
app.use((req, res, next) => {
const path = req.path;
const target = STRANGLER_CONFIG[path];
if (target === 'new_microservice') {
console.log(`Routage de ${req.method} ${path} vers le nouveau microservice`);
// Proxy de la requête vers l'URL du nouveau microservice
proxyService(req, res, 'http://localhost:3001');
} else if (target === 'legacy_monolith') {
console.log(`Routage de ${req.method} ${path} vers le monolithe hérité`);
// Proxy de la requête vers l'URL du monolithe hérité
proxyService(req, res, 'http://localhost:8080');
} else {
// Repli par défaut
next();
}
});
function proxyService(req, res, targetUrl) {
// Logique de proxy simplifiée à des fins de démonstration
// En production, utilisez des bibliothèques comme http-proxy ou nginx
const http = require('http');
const options = {
hostname: new URL(targetUrl).hostname,
port: new URL(targetUrl).port,
path: req.url,
method: req.method,
headers: req.headers
};
const proxyReq = http.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res, { end: true });
});
req.pipe(proxyReq, { end: true });
}
Points clés pour réussir
Bien que le code ci-dessus illustre le mécanisme de routage, une migration réussie nécessite plus qu'une simple répartition du trafic. Vous devez gérer soigneusement la cohérence des données. Souvent, le nouveau microservice disposera de sa propre base de données, nécessitant une synchronisation avec la base de données du système hérité pendant la période de transition. Des outils comme la réplication de base de données ou le CDC (Capture de données modifiées) peuvent aider à maintenir ces magasins de données synchronisés.
De plus, assurez-vous que vos outils de surveillance et d'observabilité couvrent à la fois les systèmes hérités et les nouveaux systèmes. Le traçage distribué devient essentiel lorsqu'une seule requête utilisateur peut s'étendre sur un module hérité et un nouveau microservice.
Conclusion
Le Pattern Strangler Fig n'est pas une solution miracle, mais c'est l'une des stratégies les plus efficaces pour moderniser les architectures complexes et monolithiques. Elle déplace l'accent des refontes risquées et à enjeux élevés vers une série de tâches de refactoring gérables et à faible risque. En étranglant progressivement le système hérité, vous préservez la continuité des activités tout en avançant vers un avenir évolutif et maintenable.
Commencez petit. Identifiez un contexte délimité unique et cohérent au sein de votre monolithe, construisez un nouveau service pour celui-ci et routez le trafic via une passerelle. À mesure que vous gagnerez en confiance, élargissez le périmètre de votre nouvelle architecture jusqu'à ce que le système hérité ne soit plus qu'un souvenir. Le voyage vers la modernisation est un marathon, pas un sprint — laissez le Strangler Fig ouvrir la voie.