Luca Mele
Luca Mele

Architecture

Micro-Frontends: Wann sie Sinn machen (und wann nicht)

Micro-Frontends: Wann sie Sinn machen (und wann nicht)
Zurück zu allen Beiträgen
·8 Min. Lesezeit

Micro-Frontends sind zu einem der meistdiskutierten Architekturmuster in der Frontend-Entwicklung geworden. Befürworter sagen, sie ermöglichen unabhängige Team-Deployments und Skalierung. Kritiker sagen, sie fügen unnötige Komplexität hinzu. Beide haben recht — die Frage ist, welcher Kompromiss für Ihre spezifische Situation wichtiger ist.

Ich habe eine einzigartige Perspektive darauf, weil ich der leitende Architekt einer Micro-Frontend-Architektur für die B2C-Strategie der AXA Schweiz war und mich in späteren Rollen bewusst gegen Micro-Frontends entschieden habe. Beide Entscheidungen waren im jeweiligen Kontext richtig.

Das Problem, das Micro-Frontends tatsächlich lösen

Micro-Frontends lösen ein Problem gut: organisatorische Unabhängigkeit. Wenn mehrere Teams Features für dieselbe benutzerorientierte Anwendung in unterschiedlichen Zeitplänen liefern müssen und die Koordination zwischen diesen Teams zum Engpass geworden ist, können Micro-Frontends helfen.

Das ist alles. Sie machen Ihre App nicht schneller (meistens das Gegenteil). Sie machen Ihren Code nicht sauberer. Sie reduzieren keine Komplexität. Sie tauschen technische Komplexität gegen organisatorische Flexibilität.

Bei AXA hatten wir genau dieses Problem. Mehrere Teams waren für verschiedene Teile der B2C-Kundenerfahrung verantwortlich: Versicherungsangebote, Schadensfälle, Policenverwaltung und Onboarding. Jedes Team hatte seinen eigenen Sprint-Rhythmus, seine eigenen Prioritäten und seinen eigenen Release-Zeitplan. Ein monolithisches Frontend bedeutete, dass ein Bug im Schadensmodul ein Release des Angebotsmoduls blockieren konnte. Teams verbrachten mehr Zeit mit der Koordination von Deployments als mit dem Entwickeln von Features.

Wie wir es bei AXA umgesetzt haben

Unser Ansatz war pragmatisch, nicht ideologisch. Wir verfolgten nicht den Microservices-Traum von «jedes Team wählt sein eigenes Framework». Wir standardisierten auf React und TypeScript — die Freiheit lag in der Deployment-Unabhängigkeit, nicht in der Technologiewahl.

Jedes Modul war eine eigenständige Anwendung, die unabhängig gebaut, getestet und deployed werden konnte. Eine schlanke Shell-Anwendung kümmerte sich um Routing, Authentifizierung und gemeinsames Layout. Module kommunizierten über einen definierten Event-Bus, nicht über direkte Imports. Der zentrale Unterschied zu Runtime-Ansätzen: die Aggregation geschah zur Build-Zeit, aber jede App konnte trotzdem unabhängig deployen. Das bedeutete, dass das Angebots-Team dreimal täglich deployen konnte, während das Schadens-Team wöchentlich deployte — ohne jegliche Koordination.

// Simplified module registration in the shell app
interface MicroFrontendModule {
  name: string;
  basePath: string;
  load: () => Promise<{ mount: (el: HTMLElement) => void }>;
}

const modules: MicroFrontendModule[] = [
  {
    name: 'quotes',
    basePath: '/insurance/quotes',
    load: () => import('quotes-module/bootstrap'),
  },
  {
    name: 'claims',
    basePath: '/claims',
    load: () => import('claims-module/bootstrap'),
  },
];

// Each module mounts into its container independently
// Shell handles routing and passes context via events

Die echten Kosten, über die niemand spricht

Hier ist, was die Konferenzvorträge auslassen:

Gemeinsamer State ist schwierig. Wirklich schwierig. Wenn zwei Micro-Frontends Benutzerkontext, Warenkorb-Status oder Benachrichtigungszähler teilen müssen, bauen Sie im Grunde ein verteiltes System im Browser. Wir haben Wochen damit verbracht, den Authentifizierungs-State zuverlässig über Module hinweg ohne Race Conditions zu synchronisieren.

Konsistente UX ist teuer. Wenn Module unabhängig deployed werden, schleichen sich visuelle Inkonsistenzen ein. Modul A wird mit aktualisierten Button-Styles ausgeliefert, während Modul B noch die alten hat. Wir lösten dies mit unserer Web-Components-basierten Style-Guide-Bibliothek — framework-agnostische Komponenten, die jedes Modul nutzte. Aber der Aufbau und die Pflege dieser Bibliothek war eine erhebliche Investition.

Der Performance-Overhead ist real. Jedes Modul bringt seinen eigenen Laufzeit-Overhead mit. Selbst wenn gemeinsame Abhängigkeiten in einen Common Chunk extrahiert werden, ist der initiale Load schwerer als bei einer einzelnen Anwendung. Bei AXA stieg unsere Time to Interactive um etwa 800ms im Vergleich zum Monolithen. Wir optimierten dies mit aggressivem Lazy Loading und Prefetching, aber der Overhead verschwand nie vollständig.

Die Developer Experience leidet. Die vollständige Anwendung lokal zu betreiben bedeutet, mehrere Dev-Server zu orchestrieren. Debugging über Modulgrenzen hinweg ist mühsam. Integrationstests erfordern, dass alle Module verfügbar sind. Wir bauten erhebliches Tooling, um dies handhabbar zu machen, was selbst einen Wartungsaufwand darstellte.

Wann ich mich dagegen entschieden habe

Bei Migros, beim Aufbau von Bikeworld und Micasa, hatten wir ein ähnliches oberflächliches Problem: mehrere Shops, die sich Infrastruktur teilen. Der Instinkt wäre vielleicht gewesen, Micro-Frontends zu verwenden. Aber ich wählte stattdessen ein PNPM-Monorepo mit Turbo.

Warum? Weil das organisatorische Problem ein anderes war. Wir hatten ein Team, das beide Shops baute, nicht mehrere Teams, die unabhängige Deployments brauchten. Die Shops teilten ein Design System und etwas Infrastruktur, hatten aber unterschiedliche Produktseiten und Checkout-Flows. Ein Monorepo mit geteilten Packages gab uns Code-Wiederverwendung ohne die Deployment-Komplexität.

Bei Vontobel, beim Aufbau einer einzelnen komplexen Finanzplattform, wären Micro-Frontends reiner Overhead gewesen. Ein Team, ein Deployment-Ziel, ein Release-Zyklus. Eine gut strukturierte Next.js-Anwendung mit klaren Modulgrenzen — durchgesetzt durch Linting-Regeln und Code Review, nicht durch Laufzeit-Isolation — war die richtige Antwort.

Bei UBS setzen wir tatsächlich Micro-Frontends mit Webpack Module Federation in 3 Teams ein. Wir haben ein Turborepo-basiertes Monorepo, aber die Module werden zur Laufzeit aggregiert — jedes Team kann unabhängig deployen. Das ist ein anderer Ansatz als bei AXA, wo die Aggregation zur Build-Zeit stattfand. Beide funktionieren, aber Runtime-Federation gibt uns schnellere unabhängige Deployments, während das Monorepo geteilten Code und Tooling konsistent hält.

Das Entscheidungs-Framework

Nachdem ich mit Micro-Frontends gelebt und Alternativen gewählt habe, hier mein Framework dafür, wann sie Sinn machen:

Verwenden Sie Micro-Frontends wenn: Sie 4+ Teams haben, die an dieselbe Anwendung liefern, Deployment-Koordination zu einem messbaren Engpass geworden ist (nicht nur ein Ärgernis), Teams wirklich unterschiedliche Release-Rhythmen haben, und Sie die Engineering-Kapazität haben, die erforderliche Infrastruktur aufzubauen und zu pflegen (Shell App, gemeinsame Bibliotheken, Tooling, CI/CD-Pipelines).

Verwenden Sie keine Micro-Frontends wenn: Sie weniger als 3 Teams haben, Sie verschiedene Frameworks pro Modul verwenden möchten (die UX-Kosten sind zu hoch), Ihre Anwendung starke modulübergreifende Datenabhängigkeiten hat, Sie keine dedizierte Platform-Engineering-Kapazität haben, oder Sie sie wählen, weil sie trendig sind, und nicht weil Sie den Schmerz gespürt haben, den sie lösen.

// Alternatives that solve similar problems with less cost

// 1. Monorepo with package boundaries
// Good for: shared code, independent builds, one team
// pnpm-workspace.yaml + turborepo

// 2. Module-level code splitting in a single app
// Good for: lazy loading, team ownership areas
const AdminModule = lazy(() => import('./modules/admin'));
const QuotesModule = lazy(() => import('./modules/quotes'));

// 3. Feature flags for independent feature releases
// Good for: decoupling deployment from release
if (flags.newCheckout) {
  return <NewCheckout />;
}

Architektur handelt von Kompromissen

Die beste Architektur ist die einfachste, die Ihre tatsächlichen Probleme löst. Nicht die Probleme, die Sie in zwei Jahren haben könnten. Nicht die Probleme, die das Unternehmen hatte, das diesen Medium-Artikel geschrieben hat. Ihre Probleme, heute.

Micro-Frontends lösten ein echtes Problem bei AXA mit Build-Time-Aggregation. Bei UBS passt Runtime-Federation mit Webpack Module Federation für 3 Teams, die aus einem Monorepo arbeiten. Ein Monorepo löste ein echtes Problem bei Migros. Eine gut strukturierte Einzelanwendung löste ein echtes Problem bei Vontobel. Das Muster hat sich nicht geändert — verstehen Sie zuerst das Problem, dann wählen Sie die einfachste Lösung, die es adressiert.

Wenn sich das nach YAGNI, angewandt auf Architektur, anhört — genau das ist es. Die Prinzipien sind auf jeder Abstraktionsebene die gleichen.