Startseite / Artikel / Browser, Server oder Build-Zeit: Eine Entscheidungsmatrix für Frontend-Architekturen

Browser, Server oder Build-Zeit: Eine Entscheidungsmatrix für Frontend-Architekturen

Sehen Sie, wie SSG, SSR, Streaming, Serverkomponenten, BFFs, Edge-Rendering, modulare Monolithen und Micro-Frontends jeweils eine Frage beantworten: Wo soll die Arbeit stattfinden?

2588 Wörter

Bei Diskussionen zur Architektur – insbesondere bei Vorstellungsgesprächen für Senior-Frontend-Entwickler – spielt es selten eine Rolle, was man bereits gebaut hat. Entscheidend ist vielmehr, warum man es auf diese Weise gebaut hat, und „Das Team hatte es bereits so“ ist keine zulässige Antwort. Die gute Nachricht ist, dass fast jedes Frontend-Architekturmuster auf dieselbe grundlegende Frage antwortet: Wie viel Arbeit soll im Browser stattfinden, wie viel auf dem Server und wie viel zur Zeit der Kompilierung? Sobald man SSR, Server Components, backends-for-frontends und Micro-Frontends als verschiedene Möglichkeiten versteht, diese Grenze zu ziehen, zwischen ihnen zu wählen und die Wahl zu rechtfertigen, wird die Entscheidungsfindung deutlich einfacher.

Ein Wort zu den Quellen: Die unten beschriebenen Unternehmen stammen aus öffentlichen technischen Berichten und offizieller Dokumentation, wobei dies entsprechend gekennzeichnet ist.

Wie sich die Grenze zwischen Client und Server verschoben hat

Das frühe Web war einfach. Statische HTML-Dateien luden sich fast sofort und es gab kaum etwas, was kaputtgehen konnte.

Dann begannen serverseitige MVC-Frameworks wie Django, Rails und ASP.NET, bei jeder Anfrage HTML zu generieren. Die Seiten konnten nun dynamische Daten anzeigen, doch jedes Klicken bedeutete ein vollständiges Neuladen der Seite.

Einfachseitenanwendungen, die mit React, Vue oder Angular entwickelt werden, haben die Situation in die andere Richtung verschoben. Der Browser übernahm das Routing, den Zustand, die Validierung und manchmal sogar die Authentifizierung. Das Ergebnis war eine Benutzeroberfläche, die sofortig erscheint – allerdings erst nach dem Laden. Dieser Aspekt ist jedoch ein Nachteil: große JavaScript-Bundles, eine langsame Erstansicht sowie eine geringere Auffindbarkeit. Google führt zwar JavaScript aus, doch bei großen Websites kann die Darstellung die Indizierung verzögern. Viele andere Crawler, einschließlich Vorabansicht-Bots in sozialen Medien und zahlreicher KI-Crawler, führen überhaupt keine Skripte aus. Inhalt, den sie nicht effektiv sehen können, existiert für sie nicht.

Von weitem betrachtet ist die Geschichte der Frontend-Architektur eine Abfolge von Wechseln zwischen dünnen Clients, bei denen der Server die meiste Arbeit übernimmt, und dicken Clients, bei denen der Browser dies tut. Jedes Muster in diesem Leitfaden stellt eine weitere Position für diese Grenze dar.

Backend-for-Frontend: Neugestaltung einer API pro Client

In einer Backend-for-Frontend (BFF)-Konfiguration betreibt das Frontend-Team einen eigenen dünnen Service vor den eigentlichen Backend-Diensten. Dieser Service erfüllt eine einzige Aufgabe: Er wandelt Backend-Daten in genau das um, was ein einzelner Client benötigt.

Dieses Muster geht in der Regel auf SoundCloud zurück. Um das Monolith-System in Microservices aufzuteilen, stellte das Unternehmen um 2013 fest, dass die Web-, iOS- und Android-Kunden alle um dieselbe gemeinsame API konkurrierten, die für keinen von ihnen geeignet war. Jeder Kunde erhielt daher einen eigenen, leichten Backend. Phil Calçado, der an dieser Migration arbeitete, beschrieb später die Geschichte detailliert, und Sam Newmans Artikel machte daraus die weithin zitierte Beschreibung dieses Musters, der es auch seinen Namen gab. Netflix kam unabhängig zu einer ähnlichen Lösung mit gerätespezifischen Adapterschichten, da eine Fernseh-App und eine Handy-App völlig unterschiedliche Datenmengen benötigen.

Betrachten wir einen konkreten Fall. Eine Mobile-App benötigt den Produktnamen, den Preis sowie ein Miniaturbild. Die Web-App braucht zusätzlich Bewertungen, Lagerbestände und verwandte Artikel. Ein einziger gemeinsamer Endpunkt liefert der Mobile-App entweder zu viel oder der Web-App zu wenig Daten, weshalb die Teams schließlich Abfragemethoden hinzufügen müssen, um dies auszugleichen.

Ein BFF löst dieses Problem, indem er jeder Anwendungsschicht eine individuell angepasste Antwort liefert. Der untenstehende Express-Dienst, der auf fetch aus Node 18 und neueren Versionen zurückgreift, ruft die übergeordnete Produkt-API auf und gibt nur vier Felder zurück – dabei wird title in name umbenannt und die Bildliste auf ein einziges Miniaturbild reduziert:

// bff.js — Node 18+, fetch is built in
import express from 'express';

const app = express();
const API = 'https://api.example.com';

app.get('/products/:id', async (req, res) => {
  const response = await fetch(`${API}/products/${req.params.id}`);
  const data = await response.json();

  res.json({
    id: data.id,
    name: data.title,
    price: data.price,
    thumbnail: data.images[0],
  });
});

app.listen(4000, () => console.log('BFF listening on :4000'));

Der Client ruft /products/123 auf und erhält genau die gewünschte Struktur – ohne überflüssige Felder und ohne zusätzliche Anfragen. In Produktionscode sollte man außerdem vor der Parsung response.ok überprüfen und auf Produkte achten, die keine Bilder enthalten, da data.images[0] voraussetzt, dass mindestens ein Bild vorhanden ist.

Der damit verbundene Aufwand verdient mehr Aufmerksamkeit, als er üblicherweise bekommt. Ein BFF ist ein weiterer Dienst, der bereitgestellt, überwacht und in Ordnung gehalten werden muss. Fällt dieser aus, funktioniert auch die Benutzeroberfläche nicht mehr – selbst wenn der eigentliche Backend-Service einwandfrei ist. Es handelt sich dabei nicht um kostenlose Infrastruktur, sondern um einen zusätzlichen Ausfallpunkt, dessen Hauptvorteil ein bequemeres Arbeiten für das Frontend-Team ist. Für eine ausführlichere Darstellung zur Implementierung eines BFF innerhalb einer Next.js-Anwendung siehe Turning Next.js Route Handlers into a Deliberate BFF Layer.

Renderungsstrategien: Wo die HTML-Dateien erstellt werden

In den letzten Jahren hat sich das Rendering stärker verändert als in jedem anderen Bereich, und in der Praxis verschwimmen die Kategorien mehr, als Diagramme vermuten lassen.

Statische Erstellung und schrittweise Regeneration

Static Site Generation (SSG) rendernt jede Seite zum Zeitpunkt der Erstellung und stellt einfache Dateien über ein CDN bereit. Es gibt nichts Schnelleres oder Günstigeres zum Bereitstellen, doch der Inhalt bleibt bis zur nächsten Veröffentlichung unverändert.

Incremental Static Regeneration (ISR) bietet eine Lösung: Eine Seite kann sich nach einem konfigurierten Zeitintervall im Hintergrund neu erstellen. So behält man den Großteil der Geschwindigkeit von statischen Dateien, ohne bei jedem Inhaltswechsel neu deployen zu müssen.

Serverseitiges Rendering und Streaming

Server-Side Rendering (SSR) erzeugt für jede Anfrage HTML. In seiner klassischen Form sendet der Server die vollständige Seite, und anschließend hydratisiert der Browser sie: Er lädt den JavaScript-Bundle herunter und fügt Ereignishandler sowie Zustände zu der bereits auf dem Bildschirm angezeigten Markup-Struktur hinzu.

React 18 führte streambasiertes SSR durch renderToPipeableStream ein, wodurch HTML-Blöcke sofort gesendet werden, sobald jeder Teil des Baums fertig ist, anstatt auf den langsamsten Komponenten zu warten. Streamen ist die übliche Wahl für neue Anwendungen, obwohl viele Produktionsanwendungen weiterhin problemlos klassisches SSR ohne Verzögerung nutzen.

Server-Komponenten, Inseln und Wiederaufnahmemöglichkeit

React Server Components (RSC) und Inselarchitekturen gehen noch einen Schritt weiter: Nur die interaktiven Teile einer Seite liefern überhaupt JavaScript. Statischer Inhalt bleibt reines HTML ohne Notwendigkeit einer Hydratierung. Astros Inselarchitekturen wenden dieses Konzept außerhalb von React an. Qwik geht noch weiter mit der Wiederaufnahmemöglichkeit, bei der die Hydratierung weitgehend vermieden wird, indem der Anwendungsstatus in das HTML serialisiert wird, sodass der Client dort weitermachen kann, wo der Server aufgehört hat.

Das untenstehende Beispiel zeigt eine Server-Component-Seite im Next.js App Router. Ab Next.js 15 ist params eine Promise, die abgewartet werden muss; deshalb wird id erst nach await params destrukturiert. Der Datensatz wird auf dem Server abgerufen, sodass Name und Preis als bereits renderter HTML-Ansicht bereitstehen:

// app/products/[id]/page.tsx (Next.js 15+)
import { fetchProduct } from '@/lib/api';

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const product = await fetchProduct(id); // runs on the server

  return (
    <main>
      <h1>{product.name}</h1>
      <p>${product.price}</p>
      <AddToCartButton productId={product.id} />
    </main>
  );
}

Nur AddToCartButton enthält Client-Side-JavaScript. Damit dies funktioniert, muss es in einer eigenen Datei liegen, die mit der 'use client'-Direktive markiert ist, und in die Seite importiert werden; aus Kürze wird dieser Import im Beispiel weggelassen. Das Ergebnis ist ein kleiner Bundle, der an den Stellen interaktiv ist, wo Interaktion wichtig ist, und andernfalls statisch bleibt.

Edge Rendering und warum die Branche teilweise umgeschwenkt ist

Edge Rendering ist eines der deutlichsten Beispiele im modernen Frontend dafür, wie eine Idee öffentlich getestet und weiterentwickelt wird.

Zwischen etwa 2021 und 2023 klang der Vorschlag überzeugend: SSR am Edge ausführen, auf Plattformen wie Cloudflare Workers oder Vercel Edge Functions, in einem Rechenzentrum in der Nähe jedes Benutzers. Kürzere Entfernung, schnellere Seiten. Vercel förderte diesen Ansatz stark.

Vercel änderte später offiziell seine Haltung; sein damaliger Vizepräsident für Produkte fasste dies als „Das hier hat mich getäuscht“ zusammen. Der Grund dafür ist aufschlussreich. Die Rechenleistung muss in der Nähe des Benutzers liegen, gleichzeitig aber auch in der Nähe der Daten – und die meisten Datenbanken befinden sich in einer einzigen Region. Eine Edge-Funktion in Tokio, die mehrere Wege zu einer Datenbank in Virginia auf sich nimmt, ist oft langsamer als eine einfache Verarbeitung in Virginia. Als Vercel dies an seinem eigenen Produkt v0 untersuchte, zeigte sich, dass eine einfache Node.js-Verarbeitung schneller war als die Edge-Verarbeitung. Vercel wandte sich daraufhin von eigenständigen Edge-Funktionen ab und empfiehlt nun den Node.js-Stack mit der Rechenleistung in derselben Region wie die Daten; zur genauen Statusbeschreibung jedes Laufzeitumfelds sollten Sie die aktuelle Plattformdokumentation konsultieren.

Was übrig geblieben ist, ist eine engere Vorstellung: Die statische Struktur einer Seite wird sofort von der Edge bereitgestellt, während die dynamischen Teile aus Rechenressourcen in der Nähe der Daten streamt werden. Das ist im Großen und Ganzen das, was Partial Prerendering leistet; der Erklärungstext zu partialer Vorkomprimierung und gleichzeitiger Komprimierung behandelt die dahinterstehenden Mechanismen.

Cloudflare Workers bleibt eine echte Edge-SSR-Plattform und funktioniert gut, wenn die Daten selbst global verteilt sind. Die wichtige Erkenntnis ist, dass die Datenlokalität in der Regel der Benutzerlokalität überlegen ist. Zu wissen, warum sich die Branche geändert hat, ist wertvoller als nur das Schlagwort zu kennen.

Der modulare Frontend-Monolith

Wenn eine Single-Page-App über einige Teams hinauswächst, wird ein flaches Repository riskant. Jeder bearbeitet dieselben gemeinsamen Komponenten, und niemand weiß genau, wem was gehört.

Ein modulares Monolith teilt die Codebasis in zwei Schichten auf, behält dabei aber nur eine für den Einsatz bereit:

  • Eine Plattformschicht, die von einem Plattformteam verwaltet wird und das Design-System, gemeinsame Hooks, Logging sowie ähnliche Infrastruktur enthält.
  • Eine Domänen-Schicht mit Funktionsordnern wie user/ oder payments/, von denen jeder von einem Funktionsteam verwaltet wird.

Die Motivation ähnelt der sauberen oder hexagonalen Architektur, ohne den größten Teil der Formalitäten. Eine vollständige saubere Architektur ist im Frontend in der Regel übertrieben; ein Button und ein Fetch-Aufruf benötigen nicht drei Abstraktions-Ebenen dazwischen. Entscheidend sind klare Verantwortlichkeiten und durchgesetzte Grenzen, beispielsweise Lint-Regeln, die verhindern, dass ein Domänenbereich die Interna eines anderen importiert.

Mikro-Frontends: Unabhängigkeit mit einem Preis

Bei einer Mikro-Frontend-Architektur wird jeder Domänenbereich als eigenständig einsetzbare Mini-Anwendung behandelt, die in der Regel zur Laufzeit von einer Shell-Anwendung über ein Mechanismus wie Webpack Module Federation geladen wird.

Was man dadurch gewinnt, ist echte Autonomie: Teams können zu ihrem eigenen Zeitplan veröffentlichen und müssen bei wirklich notwendiger Situation sogar unterschiedliche Frameworks verwenden. Sowohl Zalando, IKEA als auch DAZN haben beschrieben, wie sie dies in großem Umfang umsetzen – stets mit großen Ingenieurteams und erheblichen Investitionen in gemeinsame Tools. micro-frontends.org bleibt weiterhin die Standardreferenz für vollständige Fallbeispiele.

Der Fehlermodus, der tatsächlich Schaden anrichtet

Das Problem, das in echten Incident-Berichten immer wieder auftritt, liegt nicht im Mischen von Frameworks. Es handelt sich um shared dependency drift. Eine entfernt ausgeführte Anwendung aktualisiert eine gemeinsam genutzte Bibliothek, während eine andere das nicht tut – plötzlich laufen zwei Versionen von React auf derselben Seite und konkurrieren um denselben DOM. Module Federation kann gemeinsame Singleton-Objekte sowie Versionsbereiche deklarieren, um dies zu verhindern, allerdings nur dann, wenn die Teams sich auf diese Einschränkungen einigen und sie durchsetzen. Genau dieses Koordinationsproblem belastet die Teams weitaus stärker als die oft genannte „Komplexität“.

Auch in die entgegengesetzte Richtung gibt es ein warnendes Beispiel. Laut Berichten experimentierte Spotify vor Jahren in seiner Desktop-App mit einem auf iframes basierenden Micro-Frontend-Ansatz und konsolidierte diesen später zu einer einheitlichen Architektur – teilweise, weil die Schnittstellen zwischen den Komponenten teurer waren als die dadurch gewonnene Unabhängigkeit. Selbst in großem Maßstab ist dieses Vorgehen keine automatische Lösung.

Lassen Sie die Teamgröße die Entscheidung bestimmen

Die Frage, die selten auf Architekturdiagrammen erscheint, ist, wie viele Entwickler man tatsächlich hat. Die folgenden Bereiche sind Heuristiken, die aus den Beschreibungen der Entscheidungen durch Teams abgeleitet wurden – keine festen Regeln:

  • Kurz unter 15 Entwicklern: Ein modulares Monolith ist fast immer die bessere Wahl. Es gibt nicht genügend Personen, um getrennte Deployment-Pipelines zu rechtfertigen.
  • Ungefähr 15 bis 50 Entwickler mit klaren Bereichsgrenzen: BFFs in Kombination mit einem gut organisierten modularen Monolithen reichen in der Regel aus. Micro-Frontends sind vermutlich noch zu früh.
  • Mehr als etwa 50 Entwickler, wobei die Teams sich tatsächlich gegenseitig bei den Veröffentlichungen behindern: Hier beginnen Micro-Frontends Vorteile zu bringen – nicht weil die Anwendung gewachsen ist, sondern weil die Organisation es getan hat.
  • Eine Architekturentscheidung überzeugend erklären

    Führungskräfte erwarten eine Entscheidung, nicht einen Katalog an Optionen. Eine gute Antwort besteht in der Regel aus vier Teilen:

    1. Was Sie nutzen. Zum Beispiel: Marketingseiten werden statisch generiert, Dashboards verwenden SSR, und ein BFF steht vor der mobilen App.
  • Warum. Die statische Erstellung sorgt für eine sehr schnelle Anzeige von Inhalten, die selten ändern; SSR ermöglicht es, personalisierte Daten wie den Benutzernamen anzuzeigen, ohne dass falsche Inhalte erscheinen.
  • Der Preis, den Sie zahlen wollten. Das BFF führt eine zusätzliche Schicht ein, ermöglicht dem Frontend-Team jedoch die Kontrolle über seinen Datenschnittstelle-Vertrag, was seine Notwendigkeit rechtfertigt.
  • Was Sie abgelehnt haben und warum. Micro-Frontends wurden geprüft, doch der Koordinationsaufwand rechtfertigte sich bei der aktuellen Teamgröße nicht.
  • Der letzte Punkt ist am wichtigsten. Zu erklären, was man bewusst nicht gewählt hat, unterscheidet es von der bloßen Auflistung eines Diagramms – es geht dabei um eine echte Entscheidung. Die Umkehr der Edge-Rendering-Strategie ist ein gutes Beispiel: Selbst die Plattform, die diesen Ansatz befürwortete, änderte ihre Richtung, als die Messergebnisse nicht übereinstimmten.

    Häufige Fragen

    Inwiefern unterscheiden sich SSR und SSG?

    Weil SSR bei jeder Anfrage gerendert wird, kann es aktuelle oder benutzerbezogene Daten enthalten. SSG erstellt die HTML-Dateien einmal während des Builds und liefert statische Dateien, was schneller und günstiger ist – doch die Inhalte sind nur so aktuell wie die neueste Bereitstellung. ISR liegt dazwischen, indem es einzelne Seiten nach einem Zeitplan neu generiert.

    Lohnt sich ein BFF bei nur einem Frontend?

    In der Regel nicht. Ein BFF ist sinnvoll, wenn mehrere Clients wie Mobilgeräte, die Web-Oberfläche und eine Partner-API erheblich unterschiedliche Datenmengen von einem Backend benötigen. Bei nur einem Frontend fügt es meistens einen zusätzlichen Netzwerkschritt sowie einen weiteren zu betreibenden Dienst hinzu.

    Ist die Edge-Rendering-Technologie tot?

    Nicht unbedingt, aber die Standardeinstellung hat sich geändert. Die Bereitstellung statischer Inhalte aus dem Edge bleibt wertvoll, und Edge-Plattformen wie Cloudflare Workers überzeugen besonders dann, wenn die Daten weltweit verteilt sind. Für typische Anwendungen, die von einer Datenbank in einer einzigen Region unterstützt werden, gilt heute als Faustregel, die Rechenleistung in der Nähe der Daten statt in der Nähe des Benutzers zu platzieren.

    Wann sollte ein Team auf Micro-Frontends umsteigen?

    Nur dann, wenn die Koordination der Veröffentlichungen zwischen Teams zum eigentlichen Engpass wird – und nicht früher. Eine komplexe Anwendung, die von einem einzigen Team betreut wird, erzielt dieselben organisatorischen Vorteile wie ein modulares Monolithen-System, allerdings mit einem Bruchteil der zusätzlichen Aufwände.

    Kernpunkte

    • Jedes hier vorgestellte Muster ist eine Antwort auf eine Frage: Welche Aufgaben fallen dem Browser, dem Server und dem Build-Prozess zu?
  • Die richtige Wahl hängt stark von der Teamgröße, dem Aufbewahrungsort der Daten sowie vom Schwierigkeitsgrad der Bereitstellung ab – weitaus mehr als davon, welches Muster am beeindruckendsten klingt.
  • BFFs, Micro-Frontends und Edge-Rendering tauschen alle operative Kosten gegen einen bestimmten Vorteil ein; nennen Sie diesen Kompromiss ausdrücklich.
  • Haben Sie die Begründungen für abgelehnte Optionen bereit. Das ist der überzeugendste Beweis dafür, dass die Entscheidung bewusst getroffen wurde.