Zum Inhalt springen

Brauchen Microfrontends Microservices?

Nein. Microfrontends brauchen keine Microservices.

Microfrontends und Microservices beantworten zunächst zwei unterschiedliche Fragen. Die eine betrifft die Zerlegung und Integration des Frontends. Die andere betrifft die Zerlegung des Backends. Beide Entscheidungen können aufeinander abgestimmt werden. Sie müssen aber nicht dieselben Grenzen besitzen und schon gar nicht gemeinsam eingeführt werden.

Hinter mehreren Microfrontends kann ein klassischer Backend-Monolith stehen. Es kann ein modularer Monolith sein, eine Landschaft gemeinsam genutzter Services, eine eigene API-Fassade pro Remote oder ein vollständig eigenständiges System mit eigener Fachlogik und Datenverantwortung.

Ein gemeinsames Backend widerlegt Microfrontends nicht. Es bestimmt lediglich, wie weit ihre Autonomie reicht.

Ein Patienten-Remote, ein Kalender-Remote und ein Abrechnungs-Remote können getrennte fachliche Bereiche darstellen, von unterschiedlichen Teams entwickelt, gebaut und ausgeliefert werden und sogar in verschiedenen Hosts zum Einsatz kommen. Trotzdem können alle drei dasselbe Backend verwenden.

Ihre unabhängige Veränderbarkeit endet dann möglicherweise an der gemeinsamen Backend-Grenze. Das hebt den Nutzen der Zerlegung im Frontend nicht auf. Es zeigt nur, dass Autonomie keine binäre Eigenschaft ist. Ein Team kann sein Remote selbstständig verändern und ausliefern, bei Änderungen an der Fachlogik aber weiterhin von einem zentral verantworteten Backend abhängig sein.

Das kann ein bewusster und wirtschaftlich sinnvoller Zuschnitt sein.

Frontend- und Backend-Topologie lassen sich als zwei unabhängige Achsen betrachten:

Frontend:
monolithisch ───────────────────────── Microfrontends
Backend:
Monolith ─ modularer Monolith ─ Services ─ Self-Contained Systems

Die Position auf der einen Achse erzwingt keine bestimmte Position auf der anderen.

Ein monolithisches Frontend kann auf zahlreiche Microservices zugreifen. Umgekehrt können mehrere Microfrontends vor einem einzigen Backend stehen:

Patienten-Remote ──┐
Kalender-Remote ──┼── gemeinsames Backend
Abrechnungs-Remote ┘

Das bleibt eine Microfrontend-Architektur. Die Frontend-Bereiche können weiterhin eigene Codebereiche, Build-Prozesse, Releasezyklen und Verantwortlichkeiten besitzen.

Nicht jede Kombination ermöglicht allerdings dieselbe Reichweite an Autonomie. Ein Team, das nur das Remote besitzt, kann andere Entscheidungen treffen als ein Team, das zusätzlich den Backend-Vertrag, die Fachlogik und die zugehörigen Daten verantwortet. Daraus folgt aber keine Reifegradleiter, an deren Ende zwangsläufig Microservices stehen.

Ein modularer Monolith ist nicht automatisch eine unfertige Microservice-Architektur. Ein gemeinsames Backend ist nicht automatisch ein Architekturmangel. Und ein System mit vielen Deployments ist nicht automatisch gut geschnitten.

Die Backend-Topologie sollte daran gemessen werden, welche Verantwortung tatsächlich unabhängig ausgeübt werden soll und welchen betrieblichen Aufwand diese Unabhängigkeit rechtfertigt.

Vier mögliche Backend-Topologien hinter denselben Microfrontends.

Auch ein Backend-Monolith kann gute Grenzen besitzen

Abschnitt betitelt „Auch ein Backend-Monolith kann gute Grenzen besitzen“

Ein Monolith muss nicht zwangsläufig eine einzige globale API anbieten, über die alle Frontend-Bereiche dieselben Modelle lesen und verändern.

Auch innerhalb eines gemeinsam gebauten und ausgelieferten Backends können fachlich getrennte Verträge entstehen:

Kalender-Remote → Kalender-API
Patienten-Remote → Patienten-API
Abrechnung → Abrechnungs-API

Alle drei APIs können im selben Prozess implementiert sein und gemeinsam ausgeliefert werden. Für die Consumer ist zunächst entscheidend, ob die Verträge verständlich, stabil und fachlich passend sind – nicht, wie viele Container nach dem Deployment laufen.

Ein solcher Aufbau ist besonders plausibel, wenn die Frontend-Teams bereits getrennt arbeiten, das Backend aber weiterhin zentral organisiert ist. Dasselbe gilt für schrittweise Migrationen. Bestehende Anwendungen lassen sich selten entlang aller Schichten gleichzeitig neu schneiden. Es kann sinnvoll sein, zunächst die Frontend-Verantwortung zu trennen und das vorhandene Backend kontrolliert weiterzuverwenden.

Auch ein stabiler Backend-Bereich, der nur selten verändert werden muss, rechtfertigt nicht automatisch eine Verteilung auf mehrere Services. Zusätzliche Deployments, Netzwerkschnittstellen, Monitoring und Fehlerquellen sind kein kostenloser Ausdruck architektonischer Reinheit. Sie sollten ein konkretes Problem lösen.

Der Monolith wird nicht dadurch problematisch, dass er gemeinsam ausgeliefert wird. Problematisch wird er, wenn er keine belastbaren Grenzen anbietet: wenn jedes Remote dieselben globalen DTOs verwendet, interne Datenstrukturen direkt übernimmt oder für eine kleine Änderung von Anpassungen in zahlreichen fachlich unklaren Bereichen abhängt.

Dann fehlt nicht primär ein weiterer Service. Dann fehlt ein sinnvoller Vertrag.

Ein modularer Monolith kann die fachlichen Grenzen im Backend deutlicher machen, ohne sofort die betrieblichen Kosten verteilter Systeme zu übernehmen.

Kalender-Remote → Kalender-Modul
Patienten-Remote → Patienten-Modul
Abrechnung → Abrechnungs-Modul

Die Module können einen gemeinsamen Build und ein gemeinsames Deployment besitzen. Trotzdem können sie unterschiedliche Verantwortliche, eigene interne Modelle und klar definierte Schnittstellen haben.

Damit verbindet der modulare Monolith Eigenschaften, die in Diskussionen über Microfrontends häufig unnötig gegeneinander gestellt werden: fachliche Trennung und gemeinsamer Betrieb.

Ein Kalender-Team kann das Kalender-Remote, dessen API-Vertrag und das zugehörige Backend-Modul verantworten. Es benötigt dafür weder einen eigenen Cluster noch zwingend eine eigene Datenbank. Gleichzeitig muss es nicht jede Änderung an einem globalen Backend-Modell mit allen anderen Teams abstimmen.

Ein gemeinsames Deployment bedeutet zwar, dass Änderungen zusammen ausgeliefert werden. Es bedeutet aber nicht automatisch, dass alle Änderungen gemeinsam entwickelt, entschieden oder fachlich verantwortet werden müssen.

Der modulare Monolith kann außerdem Transaktionen und konsistente Änderungen innerhalb des Systems vereinfachen. Er erzeugt weniger verteilte Fehlerbilder und benötigt weniger betriebliche Infrastruktur als eine Landschaft eigenständig deployter Services. Diese Eigenschaften sind keine Defizite, die später überwunden werden müssen. Sie können genau zum Problem passen.

Microfrontends benötigen nicht zwingend getrennte Backend-Deployments. Häufig benötigen sie zunächst getrennte Backend-Verantwortlichkeiten.

Der Vertrag ist wichtiger als die Anzahl der Services

Abschnitt betitelt „Der Vertrag ist wichtiger als die Anzahl der Services“

Die relevante Frage lautet nicht:

Wie viele Backend-Services existieren?

Entscheidend ist vielmehr:

Kann das verantwortliche Team den Vertrag seines Remotes verstehen, verändern und stabil halten, ohne bei jeder Änderung einen organisationsweiten Releasezug auszulösen?

Ein Backend-Monolith kann einen guten Vertrag besitzen. Ein Microservice-System kann trotz zahlreicher Deployments hochgradig gekoppelt sein.

Besonders deutlich wird das, wenn ein Remote direkt auf viele interne Services zugreift:

Remote
├── Benutzer-Service
├── Kalender-Service
├── Standort-Service
├── Berechtigungs-Service
└── Abrechnungs-Service

Auf dem Architekturdiagramm wirkt dieses System fein zerlegt. Für das Remote kann es trotzdem bedeuten, dass eine einzelne Ansicht fünf synchrone Abhängigkeiten besitzt, mehrere Fehlerfälle zusammenführen und die Versionen verschiedener Services berücksichtigen muss.

Benötigt eine neue Kalenderansicht zusätzliche Benutzerdaten, Standortinformationen und Berechtigungsregeln, müssen womöglich drei oder vier Backend-Teams beteiligt werden. Das Frontend-Team besitzt zwar ein separat deploytes Remote, kann die gewünschte Funktion aber nicht unabhängig liefern.

Die Integrationskomplexität wurde dann nicht beseitigt. Sie wurde in den Browser verschoben.

Noch problematischer wird es, wenn das Remote interne Backend-Modelle direkt übernimmt. Globale Benutzer-, Standort- oder Berechtigungs-DTOs sehen zunächst nach Wiederverwendung aus. Tatsächlich machen sie interne Entscheidungen mehrerer Services zu einem Bestandteil des Frontend-Vertrags. Jede fachliche Änderung kann dadurch zahlreiche Consumer betreffen.

Microservices schützen nicht automatisch vor solchen Abhängigkeiten. Sie können sie sogar vervielfachen, wenn ihre internen Schnittstellen ungefiltert als Frontend-APIs verwendet werden.

Die Zahl der Services ist kein verlässlicher Indikator für die Qualität einer Grenze.

Ein sinnvoller Vertrag verbirgt die interne Topologie so weit, wie sie für das Remote nicht relevant ist. Das Frontend sollte nicht wissen müssen, aus wie vielen Services eine Ansicht zusammengesetzt wird. Es sollte ein Modell erhalten, das zu seiner Aufgabe passt und von einer klaren Stelle verantwortet wird.

Dabei besteht ein Vertrag nicht nur aus Endpunkten und DTO-Schemata. Zu ihm gehören auch fachliche Bedeutung, Fehlerverhalten, Kompatibilitätsregeln und die Verantwortung für seine Weiterentwicklung.

Ein Vertrag ist nicht bereits deshalb belastbar, weil ein Teamname daran geschrieben wurde. Entscheidend ist, wie er weiterentwickelt werden kann.

Ein guter Vertrag besitzt einen klaren Verantwortlichen und bekannte Consumer. Er kann abwärtskompatibel erweitert werden, behandelt Breaking Changes bewusst und zwingt nicht bei jeder Anpassung alle beteiligten Anwendungen zu einem synchronen Release.

Das setzt voraus, dass der Vertrag nicht lediglich interne Backend-Modelle nach außen spiegelt. Interne Strukturen verändern sich aus anderen Gründen als die Anforderungen eines Remotes. Werden beide Modelle gleichgesetzt, wird jede interne Umstrukturierung potenziell zur Frontend-Migration.

Auch bekannte Consumer sind wichtig. Ein Vertrag, dessen Verwendung niemand überblickt, lässt sich nur scheinbar unabhängig verändern. Aus Angst vor unbekannten Auswirkungen bleiben alte Felder dauerhaft bestehen oder Änderungen werden durch organisationsweite Abstimmungen abgesichert.

Automatisierte Vertragstests können solche Beziehungen sichtbar machen und unbeabsichtigte Inkompatibilitäten erkennen. Sie ersetzen jedoch weder eine klare Verantwortung noch eine sinnvolle Schnittstelle. Ein schlechter Vertrag wird durch mehr Tests lediglich zuverlässiger schlecht.

Ein klar besessener Vertrag ist nur dann eine belastbare Grenze, wenn er verändert werden kann, ohne alle Consumer gleichzeitig aktualisieren zu müssen.

Architekturdiskussionen konzentrieren sich gern auf sichtbare technische Grenzen: Repositories, Deployments, Container, Datenbanken und API-Endpunkte. Sie lassen sich zählen und in Diagrammen darstellen.

Ownership ist weniger sichtbar, aber meist entscheidender. Fachliche Grenzen und Ownership sind dabei kein Gegenentwurf zur Architektur, sondern ihr strategischer Teil.

Ein Aufbau aus Remotes, BFFs und Microservices hilft wenig, wenn niemand den gesamten Weg einer fachlichen Änderung verantwortet. Dasselbe gilt, wenn mehrere Teams dasselbe Modell verändern dürfen, Fehler zwischen Zuständigkeiten weitergereicht werden oder ein zentrales Backend-Team jede Anpassung freigeben und umsetzen muss.

Technische Zerlegung kann in solchen Strukturen sogar zusätzliche Übergaben erzeugen. Aus einer Änderung werden dann Tickets für mehrere Teams, obwohl die fachliche Anforderung nur einen Bereich betrifft.

Die entscheidenden Fragen sind deshalb nicht nur technischer Natur:

  • Wer besitzt den Vertrag?
  • Wer entscheidet über seine Weiterentwicklung?
  • Wer verantwortet Fehler an dieser Grenze?
  • Welche anderen Teams müssen einer Änderung zustimmen?
  • Wo endet die fachliche Verantwortung des Teams?

Ein modularer Monolith kann darauf sehr gute Antworten liefern:

Kalender-Team
├── verantwortet das Kalender-Remote
├── besitzt den Kalender-Vertrag
└── besitzt das Kalender-Modul im gemeinsamen Backend

Das Backend wird gemeinsam ausgeliefert. Die fachliche Verantwortung muss deshalb nicht gemeinsam oder unklar sein.

Umgekehrt kann ein Kalender-Microservice von einem zentralen Plattformteam betrieben werden, während das Kalender-Remote einem Produktteam gehört und der API-Vertrag von einem dritten Gremium freigegeben wird. Trotz dreier technischer Deployment-Grenzen besitzt dann niemand die Veränderung Ende zu Ende.

Deployment-Grenzen können Ownership unterstützen. Sie können es nicht ersetzen.

Ein remote-spezifisches Backend for Frontend (BFF) oder eine API-Fassade kann eine ungeeignete Backend-Grenze korrigieren, ohne das gesamte Backend neu zu schneiden.

Kalender-Remote
Kalender-BFF
├── Backend-Monolith
├── Benutzer-Service
└── Standort-Service

Das BFF kann Daten aus mehreren Quellen aggregieren und dem Remote ein Modell anbieten, das auf dessen konkrete Aufgaben zugeschnitten ist. Es kann interne Servicegrenzen verbergen, Fehler vereinheitlichen und Veränderungen der Backend-Topologie vom Frontend abschirmen.

Dadurch muss das Kalender-Remote nicht selbst wissen, welcher Service Benutzernamen liefert, wo Standorte verwaltet werden oder wie Berechtigungen technisch aufgelöst werden. Es konsumiert einen Vertrag, der für den Kalenderbereich gestaltet wurde.

Ein BFF kann außerdem Verantwortlichkeit greifbarer machen. Besitzt das Kalender-Team sowohl das Remote als auch dessen BFF, kann es den Frontend-Vertrag selbst weiterentwickeln, selbst wenn die dahinterliegenden Systeme weiterhin anderen Teams gehören.

Das ist jedoch kein Automatismus.

Ein dünner Proxy, der dieselben globalen DTOs unverändert weiterreicht, schafft noch keine fachliche Grenze. Auch ein BFF kann zahlreiche interne Services synchron koppeln, deren Fehler ungefiltert weitergeben und bei jeder Änderung mehrere Teams benötigen.

Ein BFF ist ein Werkzeug zur Vertragsgestaltung, kein Echtheitszertifikat für Microfrontends.

Bietet das bestehende Backend bereits einen geeigneten, klar verantworteten Vertrag, wäre ein zusätzliches BFF möglicherweise nur ein weiteres Deployment ohne zusätzlichen Nutzen.

Wann Microservices oder Self-Contained Systems sinnvoll werden

Abschnitt betitelt „Wann Microservices oder Self-Contained Systems sinnvoll werden“

Microservices oder vollständige Self-Contained Systems werden interessant, wenn die gewünschte Verantwortung tatsächlich weiter in das Backend reichen soll.

Remote
eigene API
eigene Fachlogik
eigene Datenverantwortung

Ein Team kann dadurch nicht nur die Darstellung und den Frontend-Vertrag verändern, sondern auch die zugrunde liegende Fachlogik selbstständig weiterentwickeln und ausliefern. Es ist weniger abhängig von einem zentralen Backend-Team und kann seinen Releasezyklus, seine Skalierung und teilweise auch seine Fehlerisolation selbst bestimmen.

Diese Reichweite kann für fachlich eigenständige Bereiche wertvoll sein. Sie kann besonders dann sinnvoll werden, wenn Backend-Anpassungen häufig benötigt werden, zentrale Teams dauerhaft zum Engpass werden oder unterschiedliche Bereiche deutlich verschiedene Anforderungen an Skalierung und Verfügbarkeit besitzen.

Die zusätzliche Autonomie hat allerdings einen Preis.

Jeder eigenständige Backend-Bereich benötigt Build- und Deployment-Prozesse, Laufzeitumgebungen, Monitoring, Logging und Verantwortliche für den Betrieb. Aus lokalen Methodenaufrufen werden Netzwerkverbindungen. Fehler können nur teilweise auftreten. Daten müssen über Systemgrenzen hinweg ausgetauscht werden. Verträge entstehen nun nicht mehr nur zwischen Frontend und Backend, sondern auch zwischen den Backend-Bereichen.

Ein Team, das einen vollständigen vertikalen Slice besitzt, erhält deshalb nicht nur mehr Freiheit. Es übernimmt auch mehr Verantwortung.

Microservices sind keine Voraussetzung für Microfrontends. Sie sind eine mögliche Antwort, wenn die gewünschte Verantwortung tatsächlich bis in das Backend und die Datenhaltung reichen soll.

Ein Microfrontend benötigt keine eigene Datenbank.

Selbst ein Team mit eigenem Backend-Modul oder eigenem Service kann zunächst auf gemeinsam verwaltete Daten zugreifen. Entscheidend ist wiederum, welche Änderungen es unabhängig durchführen können soll und welche Regeln für den gemeinsamen Besitz gelten.

Vollständige End-to-End-Autonomie endet allerdings dort, wo mehrere Bereiche dieselben fachlichen Datenstrukturen ohne klare Verantwortung verändern. Eine gemeinsame Datenbank ist deshalb nicht automatisch ein Problem. Gemeinsamer, ungeklärter Besitz ist es.

Die Datenverantwortung markiert damit eine weitere mögliche Grenze der Autonomie. Sie sollte nur dann getrennt werden, wenn der fachliche und organisatorische Nutzen den zusätzlichen Aufwand rechtfertigt.

Drei mögliche Reichweiten der Verantwortung von UI-Autonomie bis End-to-End-Autonomie.

Die passende Topologie hängt nicht davon ab, welche Architekturform als moderner gilt. Sie hängt davon ab, wie weit die unabhängige Verantwortung eines Teams reichen soll.

Bei UI-Autonomie besitzt das Team primär sein Remote:

Remote → gemeinsame API → gemeinsames Backend

Das kann ausreichen, wenn Backend-Änderungen selten sind, die vorhandene API stabil ist und gemeinsame Releases im Backend kein praktisches Hindernis darstellen.

Bei Vertragsautonomie besitzt das Team zusätzlich eine eigene API-Fassade oder ein BFF:

Remote → eigenes BFF → gemeinsames Backend

Diese Variante kann sinnvoll sein, wenn das Backend kein passendes Frontend-Modell anbietet oder das Remote mehrere interne Quellen integrieren müsste. Das Team kann seinen Vertrag selbst gestalten, ohne bereits sämtliche Fachlogik und Daten zu übernehmen.

Bei End-to-End-Autonomie reicht die Verantwortung über das Remote und den Vertrag bis zur Fachlogik und möglicherweise zu den Daten:

Remote → eigener Backend-Bereich → eigene Fachlogik → eigene Daten

Das ermöglicht einen vollständigen vertikalen Slice, bringt aber auch den höchsten betrieblichen und organisatorischen Aufwand mit sich.

Keine dieser Varianten ist allgemein überlegen. Eine größere Reichweite an Autonomie ist nur dann wertvoll, wenn sie tatsächlich genutzt wird. Ein eigener Service, dessen Änderungen weiterhin von einem zentralen Team abhängen und der gemeinsam mit allen anderen Systemen ausgeliefert wird, schafft wenig. Ein sauber verantwortetes Modul in einem gemeinsamen Backend kann dagegen sehr wirksam sein.

Für die Entscheidung sind daher konkrete Fragen hilfreicher als Architekturbegriffe:

Wie häufig benötigt das Remote Änderungen im Backend? Wird ein zentrales Team dabei regelmäßig zum Engpass? Muss das verantwortliche Team selbstständig bis zur Fachlogik liefern können? Sind gemeinsame Releases ein Problem oder sogar fachlich gewollt? Kann ein modularer Monolith bereits genügend Ownership ermöglichen? Muss das Remote heute interne Services selbst orchestrieren? Und vor allem: Wer besitzt den Vertrag und seine Weiterentwicklung?

Microfrontends brauchen keine Microservices. Sie brauchen eine Backend-Grenze, an der ihre gewünschte Autonomie nicht bei jeder Änderung wieder verloren geht.

Nicht jedes Remote braucht ein eigenes Backend. Aber jedes Remote braucht einen Vertrag, dessen Änderungen das verantwortliche Team beherrschen kann.