REST war gestern: Warum Event-Streams die Zukunft der Backend-Entwicklung sind

Von 50 Microservices auf 10: So revolutionieren Event-Streams deine Architektur
Abstract
- #REST vs Event-Streams
- #Echtzeit-Backend-Architektur
- #Moderne Web-Anwendungen
- #Event-Driven Development
- #Microservices Revolution
- #Backend-Technologien 2025
Echtzeit-Architektur: Der stille Wandel von REST zu Event-Driven Development
Stell dir vor, du bestellst in einem Restaurant. Bei der traditionellen REST-Architektur wäre das so, als würdest du alle fünf Minuten zum Kellner laufen und fragen: "Ist mein Essen schon fertig?"
Das ist nicht nur anstrengend, sondern auch ineffizient. Die neue Event-Driven Architektur funktioniert anders: Der Kellner kommt automatisch zu dir, sobald sich etwas ändert: dein Getränk ist da, die Vorspeise kommt, das Hauptgericht ist bereit.
Genau dieser Wandel findet gerade in der Softwareentwicklung statt. Und er verändert grundlegend, wie wir Backend-Systeme bauen.
Was ist eigentlich das Problem mit REST?
REST-APIs haben uns jahrelang gute Dienste geleistet. Sie sind einfach zu verstehen, gut dokumentiert und funktionieren zuverlässig. Doch die Anforderungen haben sich verändert. Nutzer erwarten heute Echtzeit-Updates, mobile Geräte müssen Datenvolumen und Akku schonen, und KI-Systeme benötigen ständig aktuelle Informationen.
Die drei größten Herausforderungen moderner Anwendungen
Die erste Herausforderung ist die Latenz-Intoleranz. Mit 5G erwarten Nutzer Reaktionszeiten unter 100 Millisekunden. Bei einer klassischen REST-Anfrage muss das Signal zum Server und wieder zurück und das dauert einfach zu lange.
Die zweite Herausforderung betrifft künstliche Intelligenz. Moderne KI-Systeme wie ChatGPT oder ähnliche Anwendungen benötigen kontinuierlich aktuelle Daten. Alle paar Sekunden JSON-Snapshots abzufragen, ist weder elegant noch performant.
Die dritte Herausforderung sind die Kosten. Jeder REST-Aufruf bedeutet Serialisierung, Netzwerk-Overhead und Deserialisierung. Bei hunderttausenden Anfragen pro Sekunde summiert sich das zu erheblichen Serverkosten.
Die neue Architektur: Client-Driven Event Streams
Die Lösung trägt noch keinen offiziellen Namen, aber das Prinzip ist revolutionär einfach. Statt vieler einzelner HTTP-Anfragen öffnet der Client eine einzige, langlebige Verbindung zum Server. Über diese Verbindung schickt der Server automatisch kleine Nachrichten, sobald sich etwas ändert.
So funktioniert es in der Praxis
Denk an einen Börsenticker. Früher hättest du die Kurse alle paar Sekunden manuell aktualisieren müssen. Heute siehst du in Echtzeit, wie sich die Zahlen verändern und ganz ohne dein Zutun. Genau dieses Prinzip übertragen wir jetzt auf die gesamte Backend-Kommunikation.
Hier ein einfaches TypeScript-Beispiel, das zeigt, wie du einen Event-Stream aufbauen kannst:
// Server-Seite: Event-Stream mit Server-Sent Events
import { EventEmitter } from 'events';
class EventStreamService {
private emitter = new EventEmitter();
// Neue Events an alle verbundenen Clients senden
publishEvent(eventType: string, data: unknown): void {
const event = {
type: eventType,
payload: data,
timestamp: Date.now(),
};
this.emitter.emit('message', event);
}
// Client verbindet sich mit dem Stream
subscribe(callback: (event: StreamEvent) => void): () => void {
this.emitter.on('message', callback);
// Rückgabe einer Cleanup-Funktion
return () => {
this.emitter.off('message', callback);
};
}
}
interface StreamEvent {
type: string;
payload: unknown;
timestamp: number;
}
// Verwendung im Server
const streamService = new EventStreamService();
// Wenn sich der Warenkorb ändert
function onCartUpdate(userId: string, cart: CartData): void {
streamService.publishEvent('cart:updated', {
userId,
items: cart.items,
total: cart.total,
});
}
Wie die Tech-Giganten es bereits umsetzen
Die größten Technologie-Unternehmen der Welt haben diesen Wandel bereits vollzogen. Ihre Erfahrungen zeigen eindrucksvoll, welche Vorteile die neue Architektur bietet.
Netflix: Live-Manifeste statt REST
Netflix hat etwa 80 Prozent des REST-Traffics durch sogenannte "Live Manifeste" ersetzt. Diese werden über WebSocket-Verbindungen und das effiziente Protobuf-Format übertragen. Das Ergebnis: 68 Prozent weniger Bandbreite und 82 Prozent geringere Latenz. Für Millionen gleichzeitiger Nutzer macht das einen gewaltigen Unterschied.
Uber: Eine Verbindung für alles
Die Fahrer-App von Uber läuft komplett über einen einzigen gRPC-Stream. Wenn ein Fahrgast eine Fahrt bucht, aktualisiert sich die Anzeige des Fahrers innerhalb von 40 Millisekunden, ohne einen einzigen klassischen REST-Aufruf. Das ist schneller, als du blinzeln kannst.
Discord: Von Anfang an echtzeitfähig
Discord hat nie auf REST für Echtzeit-Funktionen gesetzt. Der gesamte Voice- und Video-Stack basiert von Beginn an auf Event-Streams. Das erklärt, warum Discord-Anrufe so butterweich funktionieren, selbst wenn tausende Menschen gleichzeitig in einem Channel sind.
Die fünf Schichten der modernen Architektur
Um die neue Architektur zu verstehen, hilft es, sie in Schichten zu betrachten. Jede Schicht hat eine klare Aufgabe.
Schicht 1: Der Edge-Layer
Am Rand des Netzwerks, nah am Nutzer, terminiert ein Edge-Server die HTTP-Verbindung und wandelt sie in ein internes Binärprotokoll um. Das spart Bandbreite und erhöht die Geschwindigkeit.
Schicht 2: Das Gateway
Ein Gateway verteilt die Nachrichten auf verschiedene Topics. Technologien wie Kafka, NATS JetStream oder Redis Streams kommen hier zum Einsatz. Sie sorgen dafür, dass jede Nachricht zuverlässig ankommt.
Schicht 3: Die Services
Die eigentlichen Backend-Services veröffentlichen Domain-Events. Wichtig: Sie senden keine HTTP-Responses mehr, sondern ereignisbasierte Nachrichten. Ein Warenkorb-Service sendet etwa ein "ArtikelHinzugefügt"-Event.
Schicht 4: Das Client-SDK
Auf der Client-Seite abonniert ein SDK nur die Events, die wirklich relevant sind. Ein Dashboard für Bestellungen braucht keine Lager-Updates, es abonniert nur Bestellungs-Events.
Schicht 5: Die reaktive Benutzeroberfläche
Die UI aktualisiert sich automatisch, sobald neue Events eintreffen. Kein manuelles Neuladen, keine veralteten Daten, keine Flickering-Probleme.
Hier ein praktisches Beispiel für eine React-Komponente mit Event-Stream:
// Client-Seite: React Hook für Event-Streams
import { useEffect, useState, useCallback } from 'react';
interface UseEventStreamOptions {
url: string;
eventTypes: string[];
}
function useEventStream<T>(options: UseEventStreamOptions) {
const [data, setData] = useState<T | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const eventSource = new EventSource(options.url);
eventSource.onopen = () => {
setIsConnected(true);
setError(null);
};
eventSource.onerror = (err) => {
setError(new Error('Verbindung unterbrochen'));
setIsConnected(false);
};
// Für jeden Event-Typ einen Listener registrieren
options.eventTypes.forEach(eventType => {
eventSource.addEventListener(eventType, (event) => {
const parsedData = JSON.parse(event.data) as T;
setData(parsedData);
});
});
// Cleanup bei Unmount
return () => {
eventSource.close();
};
}, [options.url, options.eventTypes]);
return { data, isConnected, error };
}
// Verwendung in einer Komponente
interface CartData {
items: Array<{ id: string; name: string; price: number }>;
total: number;
}
function LiveCart() {
const { data: cart, isConnected } = useEventStream<CartData>({
url: '/api/events/cart',
eventTypes: ['cart:updated', 'cart:item-added', 'cart:item-removed']
});
if (!isConnected) {
return <div>Verbindung wird hergestellt...</div>;
}
return (
<div>
<h2>Warenkorb ({cart?.items.length ?? 0} Artikel)</h2>
<ul>
{cart?.items.map(item => (
<li key={item.id}>{item.name}: {item.price} €</li>
))}
</ul>
<strong>Gesamt: {cart?.total ?? 0} €</strong>
</div>
);
}
Die messbaren Vorteile im Überblick
Die Zahlen sprechen eine deutliche Sprache. Teams, die auf Event-Streams umgestellt haben, berichten von 60 bis 90 Prozent weniger Netzwerk-Traffic. Die Reaktionszeiten liegen unter 50 Millisekunden, selbst bei globaler Verteilung.
Ein besonders angenehmer Nebeneffekt: Cache-Invalidierung wird zum Nicht-Problem. Wenn der Server automatisch Updates pusht, braucht man keinen komplizierten Cache mehr, der ständig aktualisiert werden muss.
Auch die Versionierung vereinfacht sich erheblich. Statt REST-API-Versionen zu pflegen, nutzt man Schema-Evolution. Neue Felder werden einfach hinzugefügt, alte können parallel weiterleben.
Ein praktisches Beispiel: Vom REST-Endpoint zum Event-Stream
Schauen wir uns an, wie eine typische Migration aussehen könnte. Nehmen wir einen einfachen Bestell-Service:
// VORHER: Klassischer REST-Ansatz
// Client muss regelmäßig pollen, um Updates zu erhalten
interface Order {
id: string;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
items: OrderItem[];
updatedAt: Date;
}
// REST-Endpoint (der alte Weg)
async function getOrderStatus(orderId: string): Promise<Order> {
const response = await fetch(`/api/orders/${orderId}`);
return response.json();
}
// Client musste alle 5 Sekunden pollen
setInterval(async () => {
const order = await getOrderStatus('order-123');
updateUI(order);
}, 5000);
// NACHHER: Event-Stream-Ansatz
// Server pusht Updates automatisch
interface OrderEvent {
type: 'order:status-changed' | 'order:shipped' | 'order:delivered';
orderId: string;
newStatus: string;
timestamp: number;
metadata?: Record<string, unknown>;
}
// Event-Stream-Service (der neue Weg)
class OrderEventStream {
private eventSource: EventSource | null = null;
private handlers: Map<string, (event: OrderEvent) => void> = new Map();
connect(orderId: string): void {
this.eventSource = new EventSource(`/api/orders/${orderId}/stream`);
this.eventSource.addEventListener('order:status-changed', (e) => {
const event: OrderEvent = JSON.parse(e.data);
this.handlers.get('status-changed')?.(event);
});
}
onStatusChange(handler: (event: OrderEvent) => void): void {
this.handlers.set('status-changed', handler);
}
disconnect(): void {
this.eventSource?.close();
}
}
// Verwendung, kein Polling mehr nötig
const orderStream = new OrderEventStream();
orderStream.connect('order-123');
orderStream.onStatusChange((event) => {
console.log(`Bestellung ${event.orderId} ist jetzt: ${event.newStatus}`);
updateUI(event);
});
Wann solltest du umsteigen?
Die Umstellung auf Event-Streams lohnt sich besonders in bestimmten Szenarien. Wenn deine Anwendung Echtzeit-Funktionen benötigt, sei es ein Chat, ein Dashboard oder eine Kollaborations-Software, ist der Wechsel nahezu unvermeidlich.
Auch bei hohem Traffic zahlt sich die Investition schnell aus. Weniger Anfragen bedeuten weniger Server-Last und niedrigere Kosten. Mobile Apps profitieren zusätzlich von geringerem Datenverbrauch und längerer Akkulaufzeit.
Fazit: Die Zukunft ist ereignisbasiert
REST verschwindet nicht, weil es schlecht ist. Es wird abgelöst, weil etwas Besseres verfügbar ist. Die großen Tech-Unternehmen haben den Wechsel bereits vollzogen und ernten die Früchte: schnellere Anwendungen, zufriedenere Nutzer und niedrigere Betriebskosten.
Die gute Nachricht: Du musst nicht alles auf einmal umstellen. Beginne mit einem Feature, das von Echtzeit-Updates profitiert. Sammle Erfahrungen, miss die Ergebnisse und erweitere schrittweise. Die Werkzeuge sind ausgereift, die Dokumentation ist vorhanden, und die Community wächst stetig.
Der Wandel ist bereits im Gange. Die Frage ist nicht mehr ob, sondern wann du Teil davon wirst.
Häufig gestellte Fragen
Kann ich Event-Streams und REST parallel betreiben?
Ja, das ist sogar der empfohlene Ansatz für eine schrittweise Migration. Viele Unternehmen betreiben beide Architekturen parallel und migrieren Endpoint für Endpoint.
REST eignet sich weiterhin gut für einfache CRUD-Operationen, während Event-Streams bei Echtzeit-Anforderungen ihre Stärken ausspielen.
Ein typisches Szenario: REST für das Anlegen neuer Datensätze, Event-Streams für die Benachrichtigung über Änderungen.
Wie gehe ich mit Verbindungsabbrüchen bei Event-Streams um?
Robuste Event-Stream-Implementierungen nutzen automatische Reconnect-Mechanismen. Server-Sent Events haben dies bereits eingebaut. Der Browser versucht automatisch, die Verbindung wiederherzustellen.
Zusätzlich solltest du Event-IDs verwenden, damit der Client nach einem Reconnect mitteilen kann, welches Event er zuletzt erhalten hat. Der Server sendet dann nur die verpassten Events nach.
Für kritische Anwendungen empfiehlt sich zusätzlich ein lokaler Cache der letzten Events.
Welche Technologie eignet sich am besten für den Einstieg?
Für den Einstieg empfehle ich Server-Sent Events, da sie am einfachsten zu implementieren sind und von allen modernen Browsern unterstützt werden.
Du brauchst keine zusätzlichen Libraries und kannst mit wenigen Zeilen Code starten. Wenn du bidirektionale Kommunikation benötigst, sind WebSockets die nächste Stufe.
Für sehr hohe Performance-Anforderungen und typisierte Schemas ist gRPC mit bidirektionalen Streams die professionelle Wahl, erfordert aber mehr Einarbeitung.
- Technologien
- Programmiersprachen
- Tools