State Management oder nicht State Management?
Eine moderne Frontend-Anwendung kommt nicht mehr ohne State Management aus.
Das ist eine harte These. Absolut, provokant und natürlich zu pauschal. Eine statische Landingpage braucht kein Redux. Ein Formular mit drei Feldern auch nicht. Und wer für jeden Button-Klick einen globalen Store einführt, löst wahrscheinlich kein Architekturproblem, sondern erzeugt eins.
Trotzdem ist die These als Aufhänger hilfreich, weil sie die eigentliche Diskussion sichtbar macht.
Die entscheidende Frage lautet nicht:
Brauchen wir State Management?
Sondern:
Wo lebt unser Zustand, wer darf ihn verändern, und wie nachvollziehbar fließt er durch unsere Anwendung?
Denn Zustand gibt es immer. Die Frage ist nur, ob wir ihn bewusst modellieren – oder ob er sich zufällig über Komponenten, Services, Subscriptions, Input/Output-Ketten, Router-Parameter und lokale Variablen verteilt.

Was ist überhaupt State Management?
Abschnitt betitelt „Was ist überhaupt State Management?“State Management bedeutet nicht automatisch Redux, NgRx, Signals, Stores oder irgendeine Bibliothek.
State Management bedeutet zunächst nur:
Der Zustand einer Anwendung wird bewusst verwaltet.
Dazu gehören Fragen wie:
- Welche Daten kennt die Anwendung gerade?
- Wo werden diese Daten gehalten?
- Wer darf sie verändern?
- Wie werden Änderungen ausgelöst?
- Wie reagieren andere Teile der Anwendung darauf?
- Wie wird aus Rohdaten ein UI-Modell?
- Wie gehen wir mit Loading, Error, Success und Seiteneffekten um?
Eine Anwendung ohne bewusstes State Management hat trotzdem State. Nur liegt er dann oft irgendwo.
Ein bisschen in der Komponente.
Ein bisschen im Service.
Ein bisschen im Router.
Ein bisschen in einem Subject.
Ein bisschen in einer Subscription.
Ein bisschen im Template.
Ein bisschen in der Hoffnung, dass niemand die falsche Reihenfolge anklickt.
Das ist auch State Management. Nur eben implizit.

Welches Problem löst State Management?
Abschnitt betitelt „Welches Problem löst State Management?“State Management löst nicht das Problem, dass Anwendungen komplex sind.
Es löst ein anderes Problem:
Es macht Komplexität sichtbar, lokalisierbar und testbar.
In vielen Frontend-Anwendungen ist nicht die Fachlichkeit das eigentliche Problem. Die Fachlichkeit ist oft überschaubar:
- Datensatz laden
- Formular öffnen
- Änderung speichern
- Erfolg anzeigen
- Liste aktualisieren
- auf Detailseite navigieren
- Fehler anzeigen
Das Problem entsteht dadurch, dass diese Schritte quer über die Anwendung verteilt werden.
Die Komponente ruft den Service.
Der Service hält ein Subject.
Eine andere Komponente subscribed darauf.
Der Toast wird nebenbei ausgelöst.
Das Routing passiert in einem Callback.
Der Reload hängt an einem weiteren Callback.
Und irgendwann weiß niemand mehr, ob ein Fehler aus dem HTTP-Call, dem Mapping, dem Template oder einer alten Subscription kommt.
State Management bringt hier Ordnung hinein.
Nicht, weil ein Store magisch besseren Code erzeugt. Sondern weil er die Anwendung zwingt, über Flüsse nachzudenken.
Der eigentliche Vorteil: klare Flüsse
Abschnitt betitelt „Der eigentliche Vorteil: klare Flüsse“Ein guter State-Management-Ansatz trennt mindestens diese Dinge:
- Intent: Was will der Nutzer oder das System tun?
- Command/Event: Was wird in die Anwendung geschickt?
- Operation: Welche externe Aktion passiert, z. B. HTTP?
- Result: Welches fachliche Ergebnis entsteht?
- State Update: Wie verändert sich der Zustand?
- View Model: Was bekommt die UI zu sehen?
- Seiteneffekte: Toast, Navigation, Reload, Tracking
Dadurch entsteht ein nachvollziehbarer Ablauf:
Button click -> createIntent(payload) -> event.on(createIntent) -> http.put(payload) -> createSuccess(result) oder createError(error) -> unabhängige Reaktionen
Der Button startet dabei nicht den ganzen Prozess. Er äußert nur eine Absicht.
Die UI sagt:
Ich möchte etwas anlegen.
Sie entscheidet aber nicht selbst, welche Infrastruktur aufgerufen wird, welche Daten neu geladen werden, welcher Toast erscheint oder wohin navigiert wird. Diese Reaktionen gehören in den Anwendungsfluss, nicht in den Button-Click-Handler.
Genau hier entsteht der architektonische Vorteil.
Nicht Callback-Kette, sondern Ereignisfluss
Abschnitt betitelt „Nicht Callback-Kette, sondern Ereignisfluss“Ein häufiger Denkfehler besteht darin, Success-Reaktionen als lineare Kette zu bauen.
Also ungefähr so:
save() -> http.put() -> reload() -> showToast() -> navigate()Das funktioniert. Aber es koppelt Dinge unnötig stark aneinander.
Besser ist oft:
createSuccess(result) ├─ event.on(createSuccess) -> reload resource ├─ event.on(createSuccess) -> show toast └─ event.on(createSuccess) -> navigateDer Unterschied wirkt klein, ist aber architektonisch groß.
Im ersten Fall hängt alles an einer imperativen Kette. Im zweiten Fall gibt es ein fachliches Ergebnis, auf das mehrere Teile der Anwendung unabhängig reagieren können.

Das macht Erweiterungen einfacher.
Wenn später zusätzlich ein Analytics Event gesendet werden soll, muss nicht der bestehende Save-Flow aufgebohrt werden. Es kommt einfach eine weitere Reaktion auf createSuccess hinzu.
„Wann State Management?“
Abschnitt betitelt „„Wann State Management?““Diese Frage wird oft gestellt. Und sie ist berechtigt.
Die pragmatische Antwort lautet:
Wenn Zustand von mehr als einer Stelle gelesen oder verändert wird, lohnt sich State Management sehr schnell.
Typische Signale sind:
- Mehrere Komponenten brauchen dieselben Daten.
- Daten müssen nach Aktionen konsistent aktualisiert werden.
- Loading/Error/Success-Zustände wiederholen sich.
- Nach einem Save müssen Toast, Reload und Navigation passieren.
- Komponenten werden immer größer.
- Services enthalten plötzlich Fachlogik, UI-Logik und Cache-Logik gleichzeitig.
- Es gibt viele manuelle Subscriptions.
- Debugging besteht aus „Wo wurde das jetzt gesetzt?“.
- Tests sind schwer, weil Logik an Komponenten hängt.
Die disziplinierte Lernantwort wäre sogar härter:
Wenn du reaktiv programmieren lernen willst, nutze State Management immer.
Nicht, weil jede Anwendung es zwingend braucht. Sondern weil es trainiert, sauber zu denken:
- Daten fließen in eine Richtung.
- Änderungen passieren über explizite Ereignisse.
- UI ist Ableitung von Zustand.
- Seiteneffekte sind bewusst modelliert.
- Komponenten werden Konsumenten, nicht Steuerzentralen.
Das ist am Anfang mehr Struktur. Später ist es weniger Chaos.

Die Gegenfrage aus der Schulung
Abschnitt betitelt „Die Gegenfrage aus der Schulung“Ich habe in einer Schulung einmal mit einer einfachen Frage begonnen:
Wollt ihr in diesem Projekt reaktiv programmieren?
Wenn die Antwort „nein“ ist, müssen wir nicht über Redux, Stores oder State Management reden. Dann können wir auch imperativ weiterarbeiten und die Konsequenzen akzeptieren.
Wenn die Antwort aber „ja“ ist, dann müssen wir über State Management sprechen.
Denn reaktive Programmierung bedeutet nicht, irgendwo ein Observable oder ein signal() zu verwenden. Reaktiv wird eine Anwendung erst dann, wenn UI, Daten und Ereignisse bewusst als Flüsse modelliert werden.
Drei Implementierungen, gleiche Fachlichkeit
Abschnitt betitelt „Drei Implementierungen, gleiche Fachlichkeit“Zur Veranschaulichung habe ich denselben CRUD-Flow dreimal implementiert:
- Observer-Service-Lösung
- NgRx mit klassischem Redux-Pattern
- NgRx Signals
Die Fachlichkeit war identisch:
- Datensatz anlegen
- HTTP-Request ausführen
- Erfolg oder Fehler behandeln
- Liste aktualisieren
- Success-Toast anzeigen
- programmatisch navigieren
- UI sauber von Infrastruktur und Applikationslogik trennen
Die Architektur war in allen Varianten bewusst sauber geschnitten:
- Infrastruktur für HTTP
- Applikationsschicht für Use Cases
- State-Schicht für Zustand und Flüsse
- UI nur als Konsument
Das Interessante war nicht, dass eine Bibliothek „gewonnen“ hat.
Das Interessante war, was die Teilnehmenden bemerkt haben:
Die Fachlichkeit blieb gleich, aber der Code wurde weniger.
Nicht weil State Management immer weniger Code bedeutet. Das wäre zu einfach. Klassisches Redux kann auch sehr viel Boilerplate erzeugen.
Aber je besser das Werkzeug zum Paradigma passt, desto weniger muss man selbst erfinden.

Observer Service: funktioniert, aber man baut viel selbst
Abschnitt betitelt „Observer Service: funktioniert, aber man baut viel selbst“Ein Observer Service kann für kleine bis mittlere Fälle funktionieren.
Man hat einen Service, darin ein Subject, vielleicht ein BehaviorSubject, Methoden für Aktionen und öffentliche Streams für die UI.
Das ist nicht falsch.
Das Problem ist: Man baut sich sein eigenes kleines State Management.
Und damit auch seine eigenen Regeln:
- Wie werden Events benannt?
- Wo passiert Error Handling?
- Wo passiert Loading?
- Wer aktualisiert den Cache?
- Wer triggert Reloads?
- Wer darf
next()aufrufen? - Wo werden Seiteneffekte ausgelöst?
- Wie testet man das konsistent?
Wenn das Team sehr diszipliniert ist, kann das funktionieren. Aber Disziplin skaliert schlechter als Struktur.
Klassisches NgRx: viel Struktur, aber auch viel Zeremonie
Abschnitt betitelt „Klassisches NgRx: viel Struktur, aber auch viel Zeremonie“NgRx im Redux-Stil bringt sehr klare Regeln:
- Actions beschreiben Ereignisse.
- Reducer verändern State.
- Effects behandeln Seiteneffekte.
- Selector bereiten Daten für die UI auf.
Das ist architektonisch stark. Besonders in größeren Anwendungen.
Der Preis ist Zeremonie.
Man schreibt mehr Dateien, mehr Typen, mehr Verknüpfungscode. Für Teams, die Redux verstehen, ist das ein Vorteil. Für Teams, die nur „schnell Daten laden“ wollen, fühlt es sich oft schwer an.
Der Nutzen entsteht dann, wenn die Anwendung groß genug ist, dass diese Struktur mehr spart, als sie kostet.
NgRx Signals: weniger Code, gleiche Idee
Abschnitt betitelt „NgRx Signals: weniger Code, gleiche Idee“NgRx Signals verändert nicht die Grundidee von State Management.
Es macht sie nur näher an Angular.
State, Computed Values, Methoden und reaktive Ableitungen liegen kompakter beieinander. Viele Dinge, die früher über Actions, Reducer, Effects und Selector verteilt waren, können näher am fachlichen Modell formuliert werden.
Das kann dazu führen, dass derselbe CRUD-Flow deutlich kleiner wird.
Nicht weil die Architektur verschwindet. Sondern weil weniger Infrastruktur-Code nötig ist, um sie auszudrücken.
Die UI bleibt weiterhin Konsument. HTTP bleibt Infrastruktur. Fachliche Operationen bleiben klar modelliert. Zustand bleibt explizit.
Nur der Weg dahin ist weniger schwergewichtig.
Welches Problem kommt durch State Management neu dazu?
Abschnitt betitelt „Welches Problem kommt durch State Management neu dazu?“State Management ist kein kostenloser Gewinn.
Es bringt neue Fragen mit:
- Wo endet lokaler Component State?
- Was gehört in den Store?
- Wie granular werden Stores geschnitten?
- Wie verhindert man globale Müllhalden?
- Wie benennt man Events und Methoden konsistent?
- Wie trennt man Command State und Read State?
- Wie verhindert man, dass der Store zur neuen God Class wird?
Schlechtes State Management ist nur eine andere Form von schlechtem Code.
Ein globaler Store, in dem alles liegt, ist kein Architekturkonzept. Es ist ein Keller.

State Management hilft nur, wenn es mit klaren Grenzen kombiniert wird:
- Feature Stores statt globaler Datenhalde
- View Models statt Template-Logik
- klare Events/Intents statt beliebiger Methoden
- Seiteneffekte bewusst modellieren
- Komponenten klein halten
- Infrastruktur nicht mit UI vermischen
- Ableitungen statt manuellem Synchronisieren
Weniger Code ist nicht der wichtigste Punkt
Abschnitt betitelt „Weniger Code ist nicht der wichtigste Punkt“Weniger Code ist angenehm. Aber es ist nicht der wichtigste Vorteil.
Der wichtigste Vorteil ist:
Man weiß, wo man suchen muss.
Wenn ein Save fehlschlägt, schaue ich in den Command Flow. Wenn Daten falsch angezeigt werden, schaue ich ins View Model. Wenn ein Toast fehlt, schaue ich auf den Success-Handler. Wenn ein Reload nicht passiert, schaue ich auf die Reaktion auf das Success-Event. Wenn die UI spinnt, schaue ich zuerst, ob sie überhaupt eigene Logik enthält.
Das verbessert Debugging massiv.
Und es verbessert Tests.
Denn wenn die Komponente nur noch Zustand konsumiert und Intents sendet, muss ich weniger UI testen. Die eigentliche Logik liegt in Funktionen, Stores, Mappern und Use Cases. Das ist einfacher, schneller und stabiler testbar.

Die eigentliche Entscheidung
Abschnitt betitelt „Die eigentliche Entscheidung“Die Frage „State Management oder nicht?“ ist oft zu grob.
Besser sind diese Fragen:
- Wie komplex sind unsere Zustandsflüsse?
- Wie viele Komponenten teilen sich Daten?
- Wie oft müssen Aktionen Folgereaktionen auslösen?
- Wie wichtig ist Debugging-Nachvollziehbarkeit?
- Wie testbar soll die Fachlogik sein?
- Wie gut versteht das Team reaktive Programmierung?
- Wie groß ist die Gefahr, dass Komponenten zu Steuerzentralen werden?
In modernen Frontend-Anwendungen ist die Antwort häufig nicht mehr:
Wir brauchen kein State Management.
Sondern eher:
Wir brauchen nicht überall denselben State-Management-Level.
Lokaler UI-State darf lokal bleiben.
Ob ein Dropdown offen ist, muss nicht in einen globalen Store. Ob ein Dialog gerade sichtbar ist, vielleicht auch nicht. Ob ein Formularfeld fokussiert ist, sehr wahrscheinlich nicht.
Aber fachlicher Zustand, geladene Daten, Command-Flows, Berechtigungen, Selektionen, Filter, Ladezustände und abgeleitete View Models profitieren fast immer von expliziter Struktur.
Eine moderne Frontend-Anwendung kommt nicht mehr ohne State Management aus.
Als absoluter Satz ist das falsch.
Als architektonische Warnung ist es ziemlich nah an der Wahrheit.
Denn Zustand existiert immer. Die einzige Entscheidung ist, ob wir ihn bewusst verwalten oder ob wir ihn der Anwendung beim Wachsen überlassen.
Gutes State Management bedeutet nicht:
Wir benutzen eine Bibliothek.
Gutes State Management bedeutet:
- klare Datenflüsse
- klare Ereignisse
- klare Zuständigkeiten
- kleine UI-Komponenten
- bessere Tests
- besseres Debugging
- weniger implizite Kopplung
- weniger zufällige Architektur
Die bessere Frage ist deshalb nicht:
Ab wann brauchen wir State Management?
Sondern:
Ab wann können wir es uns leisten, keins zu haben?