Micro-Frontend-Architektur: Frontend-Entwicklung skalieren

Micro Frontends übertragen den Gedanken der Microservices auf die Frontend-Schicht. Statt einer einzigen monolithischen Anwendung wird das Frontend in kleinere, unabhängige Anwendungen zerlegt, die getrennt entwickelt, getestet und ausgeliefert werden. Jedes Micro Frontend besitzt eine eigene fachliche Domäne und kann mit anderer Technik, von anderen Teams, in anderem Takt gebaut werden.

Die Architektur entstand aus demselben Druck, der im Backend zu Microservices führte. Mit wachsender Komplexität wurden monolithische Frontends schwer wartbar, langsam im Bauen und riskant in der Auslieferung. Eine einzige Änderung irgendwo in der Codebasis verlangte, die gesamte Anwendung neu zu bauen und auszuliefern – das bremste Teams und erzeugte Reibung zwischen Gruppen, die sich unterschiedlich schnell bewegen wollten.

Was sind Micro Frontends?

Eine Micro-Frontend-Architektur teilt eine Webanwendung in fachliche Scheiben, deren jede einem eigenständigen Team gehört. Das Team verantwortet jede Schicht seiner Scheibe: die Oberflächenbausteine, die Geschäftslogik, das Laden der Daten und die Anbindung ans Backend. Der Nutzer sieht eine einzige, stimmige Anwendung, doch hinter den Kulissen besteht sie aus mehreren kleineren Anwendungen, die im selben Browserfenster laufen.

Diese Zerlegung folgt den Grundsätzen des domänengetriebenen Entwurfs. Jedes Micro Frontend entspricht einem abgegrenzten Kontext – einer logischen Grenze um eine bestimmte fachliche Fähigkeit. Kaufabschluss, Produktkatalog, Nutzerprofil und Suche können jeweils eigene Micro Frontends unter der Verantwortung eigener Teams sein.

Der entscheidende Unterschied zwischen Micro Frontends und bloßer Modulaufteilung ist, dass Micro Frontends zur Bauzeit und zur Auslieferzeit unabhängig sind. Jedes hat seine eigene Build-Strecke, sein eigenes Repository, seine eigenen Tests und seinen eigenen Auslieferungsplan. Diese Unabhängigkeit bringt die Vorteile der Architektur – und ihre Schwierigkeiten.

Ein Micro Frontend ist keine Komponentenbibliothek und keine Sammlung geteilter Hilfsfunktionen. Es ist eine eigenständige Anwendung, die zur Laufzeit in eine größere Anwendung eingefügt wird. Diese Unterscheidung ist der Schlüssel zum Verständnis von Kraft und Kosten der Architektur.

Module Federation mit Webpack 5

Module Federation ist im React- und Angular-Umfeld der verbreitetste Weg zu Micro Frontends. Mit Webpack 5 eingeführt, erlaubt sie einer JavaScript-Anwendung, zur Laufzeit Code aus einer anderen Anwendung zu laden. Der geladene Code läuft im Kontext der Wirtsanwendung und teilt Abhängigkeiten, damit Bibliotheken wie React oder Vue nicht doppelt geladen werden.

Module Federation funktioniert, indem eine entfernte Anwendung bestimmte Module freigibt und eine Wirtsanwendung sie einbindet. Die entfernte Seite erklärt, welche Module verfügbar sind, und der Wirt verweist darauf, als wären es lokale Importe. Webpack übernimmt das asynchrone Laden, die Entdopplung der Abhängigkeiten und die Versionsauflösung zur Bauzeit.

Das Teilen von Abhängigkeiten ist das wichtigste Merkmal. Nutzen Wirt und Entfernte dieselbe Bibliothek, sorgt Webpack dafür, dass nur eine Kopie im Browser landet. Das verhindert die Einbußen, die mehrere Kopien von React, Lodash oder anderen großen Bibliotheken verursachen würden. Das Singleton-Muster stellt außerdem sicher, dass geteilter Zustand – etwa ein Redux-Speicher oder ein React-Kontext – wirklich über Micro Frontends hinweg geteilt wird.

// webpack.config.js - Remote application exposing a module
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "checkout",
      filename: "remoteEntry.js",
      exposes: {
        "./CheckoutApp": "./src/bootstrap",
      },
      shared: {
        react: { singleton: true, requiredVersion: "^18.0.0" },
        "react-dom": { singleton: true, requiredVersion: "^18.0.0" },
      },
    }),
  ],
};

// webpack.config.js - Host application consuming the remote
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "shell",
      remotes: {
        checkout: "checkout@http://localhost:3001/remoteEntry.js",
      },
      shared: {
        react: { singleton: true, requiredVersion: "^18.0.0" },
        "react-dom": { singleton: true, requiredVersion: "^18.0.0" },
      },
    }),
  ],
};

// Lazy loading the remote module in the host
const CheckoutApp = React.lazy(() => import("checkout/CheckoutApp"));

function Shell() {
  return (
    <Suspense fallback={<Spinner />}>
      <CheckoutApp />
    </Suspense>
  );
}

Der Wirt lädt die Einstiegsdatei der Entfernten, sobald der Nutzer eine Route ansteuert, die das Kaufabschluss-Micro-Frontend braucht. Diese Einstiegsdatei ist eine kleine, von Webpack automatisch erzeugte JavaScript-Datei mit einem Verzeichnis aller freigegebenen Module und ihrer Orte. Fordert der Wirt ein bestimmtes Modul an, lädt Webpack das passende Stück herunter und führt es im geteilten Abhängigkeitskontext aus.

Wichtig ist, dass Wirt und Entfernte sich über die Versionen geteilter Abhängigkeiten einig sind. Verlangt die Entfernte React 18.2, der Wirt hat aber nur 18.0, scheitert das Singleton-Teilen, sofern die Versionen nicht verträglich sind. Das Feld requiredVersion nutzt semantische Versionsbereiche und gibt Spielraum, während es vor brechenden Änderungen schützt.

Einbindung über iframes

Vor Module Federation und anderen modernen Techniken waren iframes der ursprüngliche Weg. Ein iframe bettet ein vollständig eigenständiges HTML-Dokument in eine übergeordnete Seite ein. Das eingebettete Dokument hat seinen eigenen JavaScript-Kontext, seinen eigenen CSS-Geltungsbereich und seinen eigenen DOM-Baum. Diese Abschottung ist zugleich Stärke und Schwäche des Ansatzes.

iframes bieten die stärksten Abschottungsgarantien aller Techniken. CSS-Konflikte sind ausgeschlossen, weil jedes iframe ein eigenes Dokument hat. JavaScript-Kollisionen sind ausgeschlossen, weil jedes einen eigenen globalen Bereich besitzt. Kein Micro Frontend kann versehentlich den Zustand eines anderen verändern, weil die DOM-Bäume völlig getrennt sind. Ein Speicherleck in einem kann kein anderes zum Absturz bringen.

Diese Garantien haben ihren Preis. iframes sind schwer. Jedes lädt ein komplettes HTML-Dokument samt allen CSS- und JavaScript-Ressourcen. Der Browser behandelt jedes als eigenen Browsing-Kontext, was zusätzlichen Speicher, zusätzliche Netzwerkanfragen und zusätzliche Renderarbeit bedeutet. Bettet man zehn Micro Frontends in iframes ein, lädt der Browser faktisch elf Seiten.

Die Verständigung zwischen iframe und Elternseite erfordert postMessage – asynchron und auf serialisierbare Daten beschränkt. Funktionen, Klasseninstanzen oder DOM-Verweise lassen sich nicht über die Grenze reichen. Damit taugen iframes nicht für Micro Frontends, die enge Verzahnung brauchen – etwa ein Kaufformular, das auf den Warenkorbzustand der Elternseite zugreifen muss.

Auch die Zugänglichkeit leidet. Screenreader und andere Hilfsmittel tun sich mit verschachtelten Browsing-Kontexten oft schwer. Die Tastaturnavigation über iframe-Grenzen hinweg ist uneinheitlich. Die Seitensuche des Browsers greift nicht über iframe-Grenzen, und der Verlauf behandelt jede iframe-Navigation als eigene Sitzung.

iframes eignen sich am besten, um Fremdinhalte einzubetten, wo Abschottung im Vordergrund steht und Verzahnung gering ist. Für ein stimmiges Anwendungserlebnis, in dem Micro Frontends Zustand teilen, Navigation abstimmen und als eine visuelle Fläche wirken sollen, sind sie eine schlechte Wahl.

Single-spa und andere Orchestrierungsrahmen

Single-spa ist ein JavaScript-Rahmenwerk, das mehrere Micro Frontends in einer Seite dirigiert. Es verwaltet deren Lebenszyklus – einhängen, wenn der Nutzer eine passende Route ansteuert, aushängen, wenn er sie verlässt, und ungenutzte im Speicher halten, damit sie schnell wieder eingehängt werden können. Single-spa ist rahmenwerkneutral und unterstützt React, Angular, Vue, Svelte und reines JavaScript.

Jedes Micro Frontend meldet sich bei single-spa als Anwendung mit Lebenszyklusfunktionen an: bootstrap, mount, unmount und wahlweise update. Die Wurzel von single-spa steuert das Routing und entscheidet anhand von URL-Mustern, welche Anwendungen aktiv sind. Passt die URL zur Aktivitätsfunktion einer registrierten Anwendung, hängt single-spa sie ein und alle nicht mehr aktiven aus.

Single-spa löst eines der schwersten Probleme: den Ausgleich zwischen Rahmenwerken. Sind zwei Micro Frontends mit verschiedenen Versionen desselben Rahmenwerks oder mit völlig verschiedenen gebaut, sorgt single-spa dafür, dass sie konfliktfrei in derselben Seite bestehen. Erreicht wird das durch Eingriffe in die Lebenszyklushaken und durch Abschottung des globalen Zustands jeder Anwendung.

Weitere Ansätze sind Piral, das einem Plugin-Modell folgt, bei dem Micro Frontends als Module aus einem Dienst geladen werden, und Open Components mit Schwerpunkt auf serverseitiger Komposition. Daneben steht der Weg über Web Components, bei dem jedes Micro Frontend in ein eigenes Element gehüllt und bei der Custom-Elements-Schnittstelle des Browsers angemeldet wird. Web Components bringen native Abgrenzung für HTML, CSS und JavaScript mit und lassen sich deklarativ in HTML-Vorlagen zusammensetzen.

Welche Orchestrierung passt, hängt von Ihrem Technikbestand, Ihrer Teamstruktur und Ihren Leistungsanforderungen ab. Module Federation ist die beste Wahl für Teams, die ohnehin Webpack nutzen. Single-spa lohnt sich in vielsprachigen Architekturen mit unterschiedlichen Rahmenwerken. Web Components passen zu Organisationen, die rahmenwerkneutrale Grenzen durchsetzen wollen. iframes sind nur für das Einbetten fremder Inhalte angemessen.

Geteilte Komponentenbibliotheken und Designsysteme

Ein häufiger Einwand gegen Micro Frontends ist visuelle Uneinheitlichkeit. Baut jedes Team seine Oberfläche allein, sieht der Nutzer beim Wandern durch die Anwendung womöglich unterschiedliche Schaltflächen, Schriften und Layoutmuster. Die Antwort ist eine geteilte Komponentenbibliothek oder ein Designsystem, das alle Micro Frontends nutzen.

Die geteilte Bibliothek umfasst meist darstellende Bausteine wie Schaltflächen, Eingabefelder, Dialoge und Navigationselemente. Diese sind rein visuell und enthalten keine Geschäftslogik. Sie nehmen Eigenschaften für Stilvarianten, Beschriftungen und Ereignisbehandler entgegen, laden aber keine Daten, verwalten keinen Zustand und bilden kein fachspezifisches Verhalten ab. Diese Trennung hält die Bibliothek stabil und verhindert, dass sie zum Engpass für das Tempo der Teams wird.

Die Versionierung will sorgfältig geplant sein. Wird die Bibliothek als npm-Paket veröffentlicht, fixiert jedes Micro Frontend eine Version und aktualisiert nach eigenem Zeitplan. Das gibt Freiheit, kann aber zu Versionsdrift führen, bei der dasselbe Element in verschiedenen Micro Frontends unterschiedlich aussieht. Eine visuelle Regressionsprüfung für die Bibliothek hilft, solche Abweichungen vor der Produktion zu entdecken.

Alternativ liefert man das Designsystem als Laufzeitabhängigkeit aus. Die geteilte Bibliothek wird als eigenständige Anwendung bereitgestellt und von jedem Micro Frontend zur Laufzeit geladen. So nutzen alle stets die neueste Fassung jedes Bausteins, und Versionsdrift entfällt. Der Preis: Eine Aktualisierung erfordert nun eine Auslieferung, und jede brechende Änderung trifft alle Micro Frontends auf einmal.

Design-Token als kleinster gemeinsamer Nenner

Design-Token – benannte Werte für Farben, Abstände, Schrift und Schatten – sind eine leichtere Alternative zur vollen Komponentenbibliothek. Jedes Micro Frontend baut eigene Bausteine, greift aber auf dieselben Token zurück. Das lässt Freiheit in der Umsetzung und wahrt dennoch das einheitliche Bild. Token lassen sich als CSS-Eigenschaften verteilen, die leicht zu überschreiben sind und kein Build-Werkzeug erfordern.

Verständigung zwischen Micro Frontends

Micro Frontends müssen miteinander reden. Der Kaufabschluss muss wissen, was der Nutzer im Katalog in den Warenkorb gelegt hat. Die Kopfzeile muss das Benachrichtigungszeichen aktualisieren, wenn das Benachrichtigungs-Micro-Frontend eine neue Meldung erhält. Die Anmeldung muss alle anderen unterrichten, wenn sich jemand an- oder abmeldet.

Der einfachste Weg ist ein geteilter Ereignisbus. Jedes Micro Frontend kann Ereignisse auf einem globalen Kanal veröffentlichen und Ereignisse anderer abonnieren. Der Bus wird meist als Ereignissender am window-Objekt umgesetzt oder von der Hülle eingereicht. Ereignisse tragen einen Typ und eine Nutzlast; Abonnenten filtern nach Typ.

// Shared event bus - shell application
// Each micro frontend receives this bus as part of its initialization.

type BusEvent = {
  type: string;
  payload: unknown;
};

type Listener = (event: BusEvent) => void;

class EventBus {
  private listeners: Map<string, Listener[]> = new Map();

  on(type: string, listener: Listener): () => void {
    if (!this.listeners.has(type)) {
      this.listeners.set(type, []);
    }
    this.listeners.get(type)!.push(listener);
    return () => this.off(type, listener);
  }

  off(type: string, listener: Listener): void {
    const listeners = this.listeners.get(type);
    if (listeners) {
      this.listeners.set(
        type,
        listeners.filter((l) => l !== listener)
      );
    }
  }

  emit(type: string, payload: unknown): void {
    this.listeners.get(type)?.forEach((listener) => {
      listener({ type, payload });
    });
  }
}

// Usage in a micro frontend
export function init(bus: EventBus) {
  bus.on("item:added-to-cart", (event) => {
    updateCartBadge(event.payload.quantity);
  });

  bus.emit("user:logged-in", { userId: "abc-123", name: "Jane" });
}

Für komplexere Bedürfnisse ist ein geteilter Zustandsspeicher oft besser als ein Ereignisbus. Ein Redux- oder Zustand-Speicher in der Hülle lässt sich in jedes Micro Frontend einreichen. Jedes liest daraus den domänenübergreifenden Zustand und schreibt fachspezifischen Zustand in seinen eigenen lokalen Speicher. So bleibt die Kopplung lose, und für geteilte Daten wie den angemeldeten Nutzer, den Warenkorb oder die aktive Navigation gibt es dennoch eine einzige Wahrheit.

Die Verständigungsschicht sollte ausdrücklich entworfen und dokumentiert sein. Teams müssen sich über Ereignisnamen, Nutzlastformen und die Grenze zwischen geteiltem und lokalem Zustand einig werden. Ohne diese Absprache entwickeln Micro Frontends stille Abhängigkeiten vom Innenleben der anderen – und es entsteht ein verteilter Monolith mit aller Komplexität von Micro Frontends und keinem ihrer Vorteile.

Routing-Strategien

Routing in dieser Architektur muss zwei Fragen beantworten: Welchem Micro Frontend gehört die aktuelle Route? Und wie funktioniert die Navigation zwischen ihnen? Die Antworten hängen davon ab, ob Sie einen zentralen Hüllen-Router, verteiltes Routing oder eine Mischform wählen.

Beim zentralen Routing besitzt eine Hüllenanwendung den obersten Router. Sie legt die Routenkarte fest, bestimmt für jedes URL-Muster das einzuhängende Micro Frontend und reicht Routenparameter weiter. Jedes Micro Frontend hat einen eigenen inneren Router für die Navigation innerhalb seiner Domäne. Die Hülle regelt domänenübergreifende Wechsel, die Micro Frontends die Wege im Inneren.

Beim verteilten Routing gehören die Routen jedem Micro Frontend selbst. Die Hülle verwaltet weiterhin die grobe Zuordnung von URL zu Micro Frontend, doch jedes verwaltet seine Unterrouten und innere Navigation eigenständig. Eine Navigationsbibliothek in der Hülle stimmt Verlaufsänderungen ab und sorgt dafür, dass die Adresszeile den Zustand der gesamten Anwendung widerspiegelt, nicht nur des aktiven Teils.

Beim ereignisbasierten Routing koordiniert der Verständigungsbus die Navigation. Will ein Micro Frontend eine Route ansteuern, die einem anderen gehört, sendet es ein Navigationsereignis. Die Hülle hört darauf und führt den Wechsel aus, wodurch das Ziel eingehängt wird. So bleiben Routing-Fragen aus den Micro Frontends heraus und die Navigationslogik in der Hülle gebündelt.

  • Zentrales Routing: Die Hülle besitzt die Routenkarte, Micro Frontends regeln nur die innere Navigation. Am besten für kleine Teams und einfache Domänengrenzen.
  • Verteiltes Routing: Jedes Micro Frontend verwaltet seine Unterrouten. Am besten für große Teams mit klar getrennten Domänen.
  • Ereignisbasiertes Routing: Die Navigation läuft über einen geteilten Ereignisbus. Am besten für vielsprachige Architekturen mit unterschiedlichen Rahmenwerken.
  • Gemischtes Routing: Die Hülle regelt die obersten Domänenrouten, die Micro Frontends die Unterrouten. Am besten für die meisten Anwendungen im Produktivbetrieb.

Auslieferung und Versionierung

Unabhängige Auslieferung ist der Hauptgrund, Micro Frontends einzuführen. Jedes Team sollte sein Micro Frontend ausliefern können, ohne sich abzustimmen und ohne die Stabilität des Ganzen zu gefährden. Dafür müssen Auslieferungsstrecke, Hosting und Versionsschema sorgfältig entworfen sein.

Das einfachste Modell ist getrenntes Hosting. Jedes Micro Frontend wird unter eigener Adresse oder in einem eigenen Speicherbereich bereitgestellt. Die Hülle lädt es zur Laufzeit von dort. Dieses Modell gibt größtmögliche Unabhängigkeit, weil jedes Team seine Infrastruktur, seine Strecke und seine Rücknahmestrategie selbst bestimmt. Die Hülle muss bei einer Aktualisierung nicht neu ausgeliefert werden, weil sie die jeweils neueste Fassung dynamisch lädt.

Getrenntes Hosting bringt eine neue Schwierigkeit: die Abstimmung von Auslieferungen zur Wahrung der Abwärtsverträglichkeit. Erwartet der Kaufabschluss eine bestimmte Form der Eigenschaften von der Hülle und ändert die Hülle diese Form, bricht der Kaufabschluss, bis auch er aktualisiert wird. Die Antwort ist, den Integrationsvertrag – meist die von der Hülle übergebenen Eigenschaften – zu versionieren und brechende Änderungen semantisch zu kennzeichnen.

Stufenweise Einführungen und Kanarienvögel sind mit Micro Frontends leichter, weil jedes einzeln ausgeliefert werden kann. Ein Team kann eine neue Fassung an fünf Prozent der Nutzer ausrollen, Fehlerraten und Leistungswerte beobachten und den Anteil dann schrittweise erhöhen. Tritt ein Problem auf, nimmt das Team nur sein Micro Frontend zurück, ohne den Rest der Anwendung zu berühren.

Das Festnageln einer Version in der Hülle ist der Notausgang. Führt eine neue Auslieferung eine brechende Änderung ein, die im Test nicht auffiel, kann die Hülle die vorherige Fassung festnageln und die Stabilität wiederherstellen, während das Team die Ursache behebt. Umgesetzt wird das meist über eine Konfigurationsdatei oder eine Umgebungsvariable, die jedem Micro Frontend eine bestimmte ausgelieferte Fassung zuordnet.

Monorepo oder getrennte Repositories

Die Repository-Struktur für Micro Frontends ist in der Frontend-Welt ein Streitpunkt. Die Argumente für ein Monorepo und die für getrennte Repositories haben beide ihre Berechtigung; die richtige Wahl hängt von Teamgröße, organisatorischer Reife und Werkzeugvorlieben ab.

Ein Monorepo hält alle Micro Frontends in einem Repository, nach Verzeichnissen geordnet. Jedes hat seine eigene package.json, seine eigene Build-Konfiguration und sein eigenes Verzeichnis. Werkzeuge wie Turborepo, Nx, Lerna oder pnpm-Arbeitsbereiche verwalten Abhängigkeiten, führen Builds aus und koordinieren Aufgaben über Micro Frontends hinweg.

  • Geteilte Werkzeugkonfiguration ist einfach: ein ESLint-, ein TypeScript-, ein Prettier-Regelsatz für alle.
  • Übergreifende Umbauten sind leichter, weil der Code aller Micro Frontends an einem Ort liegt.
  • Die Abhängigkeitsverwaltung ist gebündelt, was Versionsabweichungen zwischen Micro Frontends verringert.
  • Atomare Commits über mehrere Micro Frontends sind möglich, wenn eine Änderung mehrere Domänen berührt.

Beim Mehr-Repository-Ansatz bekommt jedes Micro Frontend ein eigenes Repository mit eigener Strecke und eigener Werkzeugkonfiguration. Teams haben volle Hoheit über ihren Technikbestand und ihren Veröffentlichungsablauf. Ein Team, das von React zu Preact wechseln möchte, kann das ohne Abstimmung tun, solange sein Micro Frontend in der Hülle weiterhin korrekt erscheint.

  • Teams haben volle Hoheit über ihren Arbeitsablauf und ihre Werkzeugwahl.
  • Die Repository-Größe bleibt klein, wodurch das Klonen schnell und die Strecken schlank bleiben.
  • Besondere Monorepo-Werkzeuge sind nicht nötig; übliche Git-Abläufe und npm-Registrierungen genügen.
  • Eine brechende Änderung in einem Micro Frontend kann den Build eines anderen nicht zerlegen.

Der Trend geht bei Micro-Frontend-Architekturen zum Monorepo, besonders dort, wo ein gemeinsames Designsystem oder geteilte Hilfsmittel genutzt werden. Der Aufwand, Änderungen über mehrere Repositories abzustimmen, wiegt oft schwerer als der Gewinn an Eigenständigkeit – gerade bei Teams, die ohnehin nah beieinander arbeiten und ähnliche Technik einsetzen. Für verteilte Teams mit unterschiedlichen Rahmenwerken bleibt das Mehr-Repository-Modell jedoch die bessere Wahl.

Leistungsfragen

Micro Frontends bringen gegenüber einer monolithischen Anwendung von Natur aus zusätzlichen Aufwand mit. Jedes lädt sein eigenes JavaScript-Bündel, sein eigenes CSS und womöglich seine eigene Rahmenwerk-Laufzeit. Der Browser muss mehr Code herunterladen, auswerten und ausführen. Die übertragene Datenmenge steigt, und die Zeit bis zur Bedienbarkeit beim ersten Aufruf wird länger.

Das Teilen von Abhängigkeiten in Module Federation mildert das, indem Bibliotheken nur einmal geladen werden. Der Anwendungscode selbst – Bausteine, Hilfsfunktionen und Geschäftslogik jedes Micro Frontends – wird jedoch nicht geteilt. Wandert der Nutzer in einer Sitzung durch drei verschiedene Micro Frontends, lädt er den Code aller drei, selbst wenn er nur mit einem arbeitet.

Code-Aufteilung innerhalb jedes Micro Frontends ist unerlässlich. Jedes sollte seine Routen, seine schweren Bausteine und seine Fremdbibliotheken verzögert laden. Ein Micro Frontend für ein Verwaltungspanel mit einer Diagrammbibliothek sollte diese erst laden, wenn der Nutzer wirklich eine Seite mit Diagramm ansteuert. Das entspricht der Praxis in monolithischen Anwendungen, nur eben innerhalb jeder Micro-Frontend-Grenze.

Vorausladen ist eine wertvolle Optimierung. Steuert der Nutzer eine Route im Kaufabschluss an, kann die Hülle vermuten, dass als Nächstes die Bestellbestätigung folgt, und deren Ressourcen im Hintergrund holen. Die Vorauslade-Strategie sollte sich auf echte Nutzungsdaten stützen, nicht auf Annahmen über Navigationsmuster.

Der kritische Renderpfad muss geschützt bleiben. Die Hülle sollte so schnell erscheinen wie eine monolithische Anwendung. Das heißt: wenig JavaScript, wenig CSS, wenige Abhängigkeiten. Die Hülle verantwortet die erste Darstellung, und langsame Micro Frontends dürfen sie nicht aufhalten. Jedes sollte asynchron geladen werden und bis zur Bereitschaft einen Ladezustand zeigen.

Leistungsbudgets gehören auf die Ebene des einzelnen Micro Frontends, nicht nur der Gesamtanwendung. Jedes Team sollte seine Obergrenzen für Bündelgröße, Zeit bis zur Bedienbarkeit und Speicherbedarf kennen. Verstöße sollten die Strecke scheitern lassen – genau wie in einer monolithischen Anwendung.

Micro Frontends testen

Diese Architektur verlangt Tests auf drei Ebenen: die einzelnen Micro Frontends für sich, das Zusammenspiel zwischen ihnen und die zusammengesetzte Anwendung als Ganzes. Jede Ebene hat eigene Werkzeuge, eigene Ziele und eigene Zuständigkeiten.

Unit- und Komponententests innerhalb jedes Micro Frontends folgen denselben Mustern wie in jeder anderen Frontend-Anwendung. Jedes Team prüft sein eigenes, und die Tests laufen in der eigenen Strecke. Der einzige Unterschied: Man sollte annehmen, dass Hülle und andere Micro Frontends unzuverlässig sind – also vorsorglich prüfen, dass das eigene Teil sich anständig verhält, wenn die Hülle unerwartete Eigenschaften liefert oder ein Ereignis ausbleibt.

Das Zusammenspiel zu prüfen ist die schwerste Ebene. Der Test muss Hülle und mehrere Micro Frontends gleichzeitig laden, Nutzerhandlungen über Grenzen hinweg nachspielen und prüfen, ob das zusammengesetzte Verhalten stimmt. Werkzeuge wie Cypress und Playwright glänzen hier, weil sie in einem echten Browser laufen und mit der vollständig zusammengesetzten Anwendung umgehen können.

Eine wirksame Vorgehensweise ist, in einer Testumgebung eine produktionsnahe Auslieferung zu betreiben: jedes Micro Frontend unter eigener Adresse, die Hülle so eingerichtet, dass sie sie lädt. Die Testreihe läuft dann gegen diese Auslieferung und spielt echte Nutzerwege nach, die Grenzen überschreiten. So finden sich Fehler, die isoliert unsichtbar bleiben – Zeitprobleme, wenn ein Micro Frontend noch nicht bereit ist, während ein anderes es anspricht, oder Stilrückschritte, wenn eine CSS-Änderung das Layout eines anderen verschiebt.

Visuelle Regressionsprüfung ist hier besonders wichtig. Das geteilte Designsystem sollte eine eigene Reihe haben. Jedes Micro Frontend sollte eigene Prüfungen für seine Seiten haben. Und die zusammengesetzte Anwendung sollte solche Prüfungen für die entscheidenden Nutzerwege besitzen. Werkzeuge wie Percy, Chromatic oder Loki fügen sich in die Strecke ein und finden Abweichungen selbsttätig.

Wann man Micro Frontends NICHT nutzen sollte

Micro Frontends sind nicht für jedes Vorhaben die richtige Architektur. Die Komplexität, die sie mitbringen – Betriebsaufwand, Leistungskosten, Abstimmungsbedarf und schwierigere Fehlersuche –, rechtfertigt sich nur durch bestimmte organisatorische Bedürfnisse. Wer sie einem Projekt überstülpt, das sie nicht braucht, erzeugt Reibung und bremst die Entwicklung ohne Gegenwert.

Ein kleines Team, das eine einzige Anwendung baut, braucht sie nicht. Die Architektur ist für Organisationen mit mehreren Teams gedacht, die je eine eigene Domäne verantworten. Lässt sich Ihr gesamtes Frontend von drei oder vier Entwicklern bauen, ist eine monolithische Anwendung mit gut geordneten Modulen produktiver, schneller zu bauen und leichter zu pflegen.

Eine Anwendung mit eng verzahnten Domänen ist ein schlechter Kandidat. Greift jede Funktion auf jede andere zu – sind Katalog, Warenkorb und Kaufabschluss so verwoben, dass sie sich nicht sauber trennen lassen –, dann zwingt Sie die Aufteilung dazu, aufwendige Verständigungsschichten zu bauen, die den direkten Zugriff aus dem Monolithen nachahmen. Das ist das Muster des verteilten Monolithen.

Einer Organisation ohne betriebliche Reife werden Micro Frontends schwerfallen. Die Architektur verlangt gute DevOps-Praxis, automatisierte Tests, Überwachung und geübte Störungsbearbeitung. Jedes Team muss eigenständig ausliefern und schnell zurücknehmen können. Fehlen diese Grundlagen, ist es die bessere Investition, sie zunächst im Rahmen einer monolithischen Anwendung aufzubauen.

Leistungskritische Anwendungen mit strengen Ladezeitvorgaben vertragen den Zusatzaufwand womöglich nicht. Wenn jede Millisekunde zählt – etwa in einem Handelsarbeitsplatz in Echtzeit oder einer Videooberfläche –, können die zusätzlichen Netzwerkanfragen und die längere JavaScript-Auswertung das Budget sprengen.

  • Ihr Team hat weniger als vier Frontend-Entwickler.
  • Die Anwendung hat weniger als fünf klar unterscheidbare Domänengrenzen.
  • Das Team hat keine Erfahrung mit Microservices oder verteilten Systemen.
  • In der Organisation fehlen automatisierte Auslieferung und Überwachung.
  • Die Anwendung hat strenge Leistungsbudgets, die ohnehin schwer einzuhalten sind.
  • Die Funktionen sind eng verzahnt und lassen sich nicht sauber in Domänen trennen.

Micro Frontends sind eine organisatorische Entscheidung, die sich als technische Architektur zeigt. Brauchen Sie die organisatorische Eigenständigkeit nicht – verschiedene Teams, verschiedene Takte, verschiedene Technikwahl –, dann lohnt der technische Aufwand nicht.

Fazit

Micro Frontends sind ein starkes Muster für Organisationen, die Frontend-Entwicklung über mehrere Teams skalieren müssen. Die Architektur bringt echte Vorteile: unabhängige Auslieferbarkeit, Eigenständigkeit der Teams, Freiheit in der Technikwahl und die Möglichkeit, Funktionen in verschiedenem Takt zu veröffentlichen. Diese Vorteile sind real und messbar, wenn die Architektur auf das richtige Problem angewandt wird.

Der Schlüssel zum Erfolg liegt darin, sie zuerst als organisatorische und erst dann als technische Lösung zu sehen. Die Architektur existiert, um Teams unabhängig zu machen, nicht um elegant entkoppelten Code hervorzubringen. Jede technische Entscheidung – Module Federation oder iframes, Monorepo oder Mehr-Repository, Ereignisbus oder geteilter Speicher – sollte dem Ziel dienen, das Tempo der Teams zu erhöhen und zugleich ein stimmiges Nutzererlebnis zu wahren.

Fangen Sie klein an. Beginnen Sie mit einem einzigen Micro Frontend, das eine klare Domänengrenze hat und ein Team, das es eigenständig verantworten will. Weisen Sie nach, dass die Auslieferung funktioniert, die Leistung reicht und das Team schneller liefert. Erweitern Sie dann schrittweise. Micro Frontends sind kein Alles-oder-nichts: Eine Mischform – ein oder zwei Micro Frontends in einer ansonsten monolithischen Anwendung – ist oft der beste Anfang.

Der häufigste Fehlschlag ist verfrühte Abstraktion. Teams bauen ausgefeilte Orchestrierungsschichten, geteilte Zustandssysteme und übergreifende Infrastruktur, bevor sie ihre tatsächlichen Bedürfnisse kennen. Beginnen Sie mit der einfachsten möglichen Einbindung – einer Hülle, die Micro Frontends per Skript-Tag oder Module Federation lädt – und fügen Sie Komplexität erst hinzu, wenn sich der einfache Weg als unzureichend erweist. Die passende Abstraktionshöhe zeigt sich im tatsächlichen Gebrauch, nicht im Entwurf vorab.

Module Federation hat Micro Frontends zugänglicher gemacht als je zuvor, doch die Grundfragen bleiben organisatorisch. Müssen Ihre Teams unabhängig ausliefern? Kann Ihre Organisation die betriebliche Komplexität tragen? Lassen sich Ihre Domänen sauber trennen? Wer diese Fragen bejaht, wird Micro Frontends als Umbruch erleben. Wer sie verneint, wird sie als Ärgernis erleben. Die Architektur selbst ist neutral – ihr Wert hängt ganz vom Zusammenhang ab, in dem sie angewandt wird.

See what your own repository can account for.

Thirty minutes on a repository you choose, including the part the record cannot attribute.