Das Strangler-Feigen-Muster: Eine sichere Möglichkeit, monolithische Anwendungen zu migrieren

Das Strangler-Feigen-Muster: Eine sichere Möglichkeit, monolithische Anwendungen zu migrieren

In der modernen Softwareentwicklung sind veraltete monolithische Anwendungen eine häufige Herausforderung. Mit der Zeit wird eine erfolgreiche Codebasis so groß und vernetzt, dass einfache Änderungen riskant werden, Bereitstellungen Stunden dauern und die Skalierung einzelner Funktionen praktisch unmöglich ist.

Wenn Teams beschließen, ihre Systeme durch die Migration auf Microservices zu modernisieren, stehen sie vor einer wichtigen Frage: Wie schreiben wir das System neu, ohne unser aktuelles Geschäft zu zerstören?

Eine Option ist eine „Big Bang“-Umschreibung – der Aufbau des neuen Systems von Grund auf hinter verschlossenen Türen und die Umstellung an einem einzigen Tag. Dies ist jedoch äußerst riskant und führt häufig zum Scheitern.

Glücklicherweise gibt es eine sicherere und zuverlässigere Alternative: das Würge-Feigen-Muster. In diesem Leitfaden werden wir untersuchen, was das Strangler Fig Pattern ist, warum es funktioniert und wie man es Schritt für Schritt mithilfe klarer Diagramme und realem Code anwendet.


Die Analogie zur realen Welt: Die Würgefeigenpflanze

Das Muster ist nach der Würgefeige benannt, einer Pflanze, die in tropischen Regenwäldern heimisch ist.

Ein Würgefeigensamen keimt in den oberen Zweigen eines bestehenden „Wirtsbaums“. Anstatt vom Boden nach oben zu wachsen, wächst die Feige nach unten:

  1. Es schickt Wurzeln entlang des Stammes des Wirtsbaums, bis sie den Waldboden erreichen und sich im Boden verankern.
  2. Mit der Zeit wachsen weitere Wurzeln, die sich um den Wirt schlingen und miteinander verschmelzen.
  3. Die Feige bildet Blätter, die das Licht daran hindern, den Wirtsbaum zu erreichen.
  4. Schließlich stirbt der Wirtsbaum ab und verrottet, sodass an seiner Stelle ein hohler Würgefeigenbaum zurückbleibt.

In der Softwarearchitektur ist der alte Monolith der Hostbaum, und die neuen Microservices sind die Würgefigur. Wir bauen die neuen Dienste an den Rändern des Monolithen auf und verlagern den Datenverkehr schrittweise vom Altsystem weg, bis der Monolith vollständig abgeschaltet werden kann.


Warum „Big Bang“-Umschreibungen scheitern

Bevor wir uns mit der Mechanik des Strangler Fig-Musters befassen, wollen wir verstehen, warum die Alternative – eine komplette Neufassung – so gefährlich ist:

  • Kein Wert für Monate (oder Jahre): Entwickler verbringen viel Zeit damit, Code zu schreiben, aber nichts davon wird live geschaltet, bis das gesamte Projekt abgeschlossen ist.
  • Scope Creep: Während einer zweijährigen Neufassung ändern sich die Geschäftsanforderungen. Das Ziel wird verschoben und das neue System muss Funktionen unterstützen, die zu Beginn der Umschreibung noch nicht vorhanden waren.
  • Fehlendes implizites Verhalten: Monolithen enthalten jahrelange undokumentierte Fehlerbehebungen und Randfallbehandlungen. Bei einer reinen Neufassung werden diese Details oft vergessen.
  • Hohes Bereitstellungsrisiko: Das gleichzeitige Ausschalten eines riesigen Altsystems und das Einschalten eines neuen Systems führt zu einem enormen Explosionsradius, falls etwas schief geht.

So funktioniert das Strangler-Feigen-Muster

Die Kernidee des Strangler Fig Pattern ist die inkrementelle Migration. Anstatt das gesamte System zu migrieren, migrieren Sie jeweils ein kleines Feature oder „Slice“.

Diagramm der Migrationsstadien des Strangler-Feigenmusters

Der Migrationsprozess wird in fünf Hauptphasen durchgeführt:

1. Identifizieren Sie einen begrenzten Kontext

Schauen Sie sich Ihren Monolithen an und identifizieren Sie eine einzelne, eigenständige Geschäftsfähigkeit, die leicht zu extrahieren ist. Zu den guten Kandidaten gehören:

  • Funktionen, die sich häufig ändern (damit das Team schnell von unabhängigen Bereitstellungen profitiert).
  • Einfache, risikoarme Funktionen (wie ein statischer FAQ- oder Benutzereinstellungsbereich) zum Testen der Migrationspipeline.
  • Funktionen mit sauberen, klar definierten Datenbankgrenzen.

2. Implementieren Sie den neuen Microservice

Bauen Sie die identifizierte Funktion als brandneuen, modernen Microservice auf. Dieser Dienst verfügt über eine eigene Datenbank, eine eigene Bereitstellungspipeline und basiert auf modernen Tech-Stacks. Entscheidend ist, dass die alte Funktion im Monolith vorerst aktiv und unverändert bleibt.

3. Führen Sie die Interception Layer ein

Um die Migration für Ihre Benutzer transparent zu machen, führen Sie vor der Anwendung eine Interception Layer (z. B. ein API Gateway oder einen Reverse Proxy) ein. Der gesamte Clientverkehr geht jetzt zuerst an dieses Gateway.

Zunächst leitet das Gateway 100 % aller Anfragen an den Legacy-Monolithen weiter.

4. Den Datenverkehr schrittweise umstellen

Sobald der neue Microservice vollständig getestet und bereit ist, aktualisieren Sie die Routing-Regeln im Interception Layer. Anstatt Anfragen für die migrierte Funktion (z. B. /api/users) an den Monolithen weiterzuleiten, leitet das Gateway sie an den neuen Mikroservice weiter.

Alle anderen Anfragen gehen weiterhin an den Monolithen. Wenn der neue Dienst ausfällt oder Fehler aufweist, können Sie das Gateway schnell aktualisieren, um den Datenverkehr zurück zum Monolithen zu leiten und so Störungen auf ein Minimum zu reduzieren.

5. Außerbetriebnahme und Wiederholung

Nachdem der neue Microservice eine Zeit lang stabil läuft, können Sie den entsprechenden Code sicher im alten Monolithen löschen.

Anschließend wählen Sie die nächste Funktion aus und wiederholen den Vorgang. Mit der Zeit schrumpft der Monolith, bis er keinen Datenverkehr mehr hat und Sie den Legacy-Server vollständig außer Betrieb nehmen können.


Die Abfangschicht: Express-Routing-Beispiel

Das Herzstück des Strangler Fig-Musters ist die Interception Layer. Es ermöglicht Ihnen, Anfragen umzuleiten, ohne die Client-Anwendungen (Web-Apps, mobile Apps) zu ändern.

Hier ist ein praktisches Node.js-Beispiel mit einem Express-basierten Gateway-Proxy. Es leitet Anfragen dynamisch weiter: Eingehende Anfragen werden an die neuen Dienste weitergeleitet, wenn sie mit migrierten Pfaden übereinstimmen. andernfalls greifen sie auf den alten Monolithen zurück.

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
const PORT = 8080;

// Configuration: Target server URLs
const LEGACY_MONOLITH_URL = 'http://legacy-monolith-server:3000';
const NEW_USER_SERVICE_URL = 'http://new-user-service:3001';
const NEW_PAYMENT_SERVICE_URL = 'http://new-payment-service:3002';

// Simple logging middleware to track traffic distribution
app.use((req, res, next) => {
    console.log(`[ROUTE LOG] Incoming request: ${req.method} ${req.url}`);
    next();
});

// 1. MIGRATED: User registration & profile requests route to the new microservice
app.use('/api/users', createProxyMiddleware({
    target: NEW_USER_SERVICE_URL,
    changeOrigin: true,
    pathRewrite: {
        '^/api/users': '/v1/users', // Translate path format if necessary
    }
}));

// 2. MIGRATED: Payment transactions route to the new payment microservice
app.use('/api/payments', createProxyMiddleware({
    target: NEW_PAYMENT_SERVICE_URL,
    changeOrigin: true
}));

// 3. FALLBACK: All other legacy routes automatically default to the Monolith
app.use('/', createProxyMiddleware({
    target: LEGACY_MONOLITH_URL,
    changeOrigin: true
}));

app.listen(PORT, () => {
    console.log(`Interception Gateway routing traffic successfully on port ${PORT}`);
});

Umgang mit der Datenbank: Der schwierigste Teil

Während die Weiterleitung des API-Verkehrs relativ einfach ist, ist die Datenverwaltung der schwierigste Aspekt der monolithischen Migration. Ein Monolith verfügt normalerweise über eine einzelne, riesige Datenbank, in der Tabellen stark verknüpft sind.

Wenn Sie einen Dienst extrahieren, müssen Sie auch seine Daten extrahieren. Es gibt zwei gängige Ansätze, um damit umzugehen:

  1. Dual Writes: Die Abfangschicht oder die Anwendung schreibt während der Übergangsphase gleichzeitig Daten sowohl in die Legacy-Datenbank als auch in die neue Dienstdatenbank. Dadurch bleiben beide Datenbanken synchron.
  2. Change Data Capture (CDC): Ein Tool wie Debezium überwacht das Transaktionsprotokoll der Legacy-Datenbank und überträgt Änderungen automatisch nahezu in Echtzeit an die neue Microservice-Datenbank.

Sobald Sie sicher sind, dass die Datenbanken vollständig synchronisiert sind und die neue Datenbank korrekt ist, schalten Sie den Leseverkehr auf den neuen Dienst um und fahren die alten Tabellen herunter.


Vergleich: Big Bang Rewrite vs. Strangler Abb

Merkmal/Metrik Big Bang Rewrite Würgefeigenmuster
Risikostufe Extrem hoch Niedrig und verwaltet
Feedback-Schleife Sehr langsam (nur am Ende) Schnell (kontinuierliche Produktionsprüfung)
Rollback-Strategie Schwer (erfordert die Wiederherstellung von Backups) Einfach (Routenregeln im Gateway ändern)
Geschäftliche Auswirkungen Disruptiv Keine Ausfallzeiten
Systemkomplexität Hoch (während der Build-Phase) Hoch (während der Migrationsphase)
Bereitstellungszeit Riesige Single-Veröffentlichung Kleine, häufige Updates

Abschluss

Das Strangler Fig Pattern ist der Industriestandard für die Migration von Monolithen zu Microservices. Indem Sie Legacy-Code schrittweise und nicht auf einmal ersetzen, eliminieren Sie das Risiko eines „Big Bang“-Fehlers.

Es hält die Bereitstellungen klein, liefert sofortiges Feedback vom tatsächlichen Produktionsverkehr und ermöglicht Ihrem Team, während des gesamten Migrationslebenszyklus weiterhin geschäftlichen Mehrwert zu liefern.

Während die Verwaltung der Datenbankmigration und der Betrieb zweier paralleler Systeme die betriebliche Komplexität erhöhen, ist es aufgrund der Sicherheit, Vorhersehbarkeit und Stabilität die bevorzugte Wahl für moderne Cloud-Migrationen.


Weitere Einblicke in Softwareentwicklung und Backend-Engineering finden Sie im Ghaznix-Blog →