+State – wo die Fachlichkeit lebt
Der Infrastructure-Artikel hat aus einem rohen "ACTIVE" ein eigenes CustomerStatus.Active gemacht.
Mehr aber auch nicht.
Wir wissen damit noch nicht, ob dieser Kunde bestellen darf. Wir wissen nicht, welche Aktionen gerade zur Verfügung stehen, welcher Zustandsübergang erlaubt ist oder welche Auswahl für einen fachlichen Vorgang relevant ist.
Hier beginnt +State.
Infrastructure übersetzt die Außenwelt in eine kontrollierte eigene Repräsentation. +State setzt diese Werte in Beziehung und interpretiert sie im Kontext unserer fachlichen Welt.
Ein Store ist mehr als ein Datencontainer
Abschnitt betitelt „Ein Store ist mehr als ein Datencontainer“Ein +State sieht auf den ersten Blick oft harmlos aus:
type CustomerState = { customers: Customer[]; selectedCustomerId?: CustomerId; loading: boolean;};Für sich genommen enthält dieser State zunächst Werte. Interessant wird er durch die Fragen, die sich mit diesen Werten beantworten lassen:
Welcher Kunde ist ausgewählt?Ist die Auswahl noch gültig?Welche Kunden erfüllen eine bestimmte Bedingung?Welche Aktionen sind aktuell erlaubt?Was folgt aus einem Zustandswechsel?Nicht jede dieser Fragen ist automatisch eine Business Rule. Auch nicht jedes Feld eines Stores ist zwangsläufig fachlicher Zustand. Ein loading-Flag bleibt beispielsweise technischer Laufzeitzustand – auch wenn es, wie der Denkzettel Loading ist kein Boolean zeigt, selten ein einfacher Schalter ist.
Der entscheidende Unterschied liegt darin, dass +State nicht beim Speichern von Werten aufhört. Hier entstehen Zustandsübergänge, Ableitungen und fachliche Entscheidungen aus dem aktuellen Zustand der Anwendung.
State bekommt durch seine Beziehungen, Regeln und Ableitungen Bedeutung.

Ein Store ist kein Client-Side-Cache
Abschnitt betitelt „Ein Store ist kein Client-Side-Cache“Eine sehr einfache Vorstellung von State Management sieht so aus:
API ↓Store speichert Response ↓Component liest ResponseDas kann völlig ausreichend sein. Architektonisch ist der Store in diesem Modell allerdings kaum mehr als ein Cache für Backend-Daten. Er hält Werte bereit, ohne ihnen eine eigene Bedeutung zu geben.
Unser Verständnis von +State geht weiter:
Infrastructure ↓ +State ├── besitzt Zustand ├── verarbeitet fachliche Intents ├── wendet Business Rules an ├── modelliert Zustandsübergänge └── erzeugt AbleitungenOb dafür Signals, NgRx, Redux, Zustand, RxJS oder ein anderes State-Konzept verwendet wird, ändert an dieser Verantwortung nichts.
Die Library entscheidet, wie State verwaltet wird. Die Architektur entscheidet, welche Verantwortung dieser State trägt.
Hier leben Business Rules
Abschnitt betitelt „Hier leben Business Rules“Infrastructure hat übersetzt:
"ACTIVE" ↓CustomerStatus.Active+State kann diesen Zustand jetzt gemeinsam mit weiteren Informationen auswerten:
CustomerStatus.Active+CreditHold.None+Account.enabled ↓ canOrderDie Frage, ob ein Kunde bestellen darf, ist keine technische Übersetzung mehr. Sie beschreibt eine Regel unseres Systems.
Dasselbe gilt für Fragen wie:
- Darf ein Auftrag storniert werden?
- Darf ein Datensatz gelöscht werden?
- Ist ein Zustandsübergang zulässig?
- Welche Aktion steht in der aktuellen fachlichen Situation zur Verfügung?
- Erfüllt eine Entity eine bestimmte fachliche Bedingung?
Eine Business Rule wird nicht zu Presentation-Logik, nur weil ihr Ergebnis später einen Button deaktiviert.
Gerade dort schleichen sich fachliche Regeln trotzdem häufig ein:
disabled = customer.status !== CustomerStatus.Active || customer.creditHold !== CreditHold.None;Solange nur eine Oberfläche existiert, wirkt das harmlos.
Spätestens wenn dieselbe Entscheidung zusätzlich in einer Ionic-Presentation, einer mobilen Oberfläche oder an einer anderen Stelle benötigt wird, existieren mehrere Implementierungen derselben Regel.
Der fachliche Zustand kann stattdessen eine zentrale Aussage bereitstellen:
canOrder;Die Presentation entscheidet anschließend, wie sie diesen Zustand darstellt: als deaktivierten Button, ausgegraute Aktion oder gar nicht sichtbaren Menüeintrag.
Ob die Aktion fachlich erlaubt ist, entscheidet sie nicht erneut.
Fachlicher Zustand und Derived State
Abschnitt betitelt „Fachlicher Zustand und Derived State“Nicht jede Information muss zusätzlich gespeichert werden.
Aus:
customers+selectedCustomerId ↓selectedCustomerkann selectedCustomer abgeleitet werden.
Dasselbe gilt beispielsweise für:
customers+filter+sort ↓visibleCustomersselectedCustomer und visibleCustomers müssen nicht als weitere Kopien ihres Ursprungszustands gehalten und bei jeder Änderung manuell synchronisiert werden.
Sie können aus dem vorhandenen State entstehen.
Das reduziert doppelte Wahrheit und die damit verbundenen Synchronisationsprobleme. Sobald zwei gespeicherte Werte dieselbe Information ausdrücken, muss irgendein Mechanismus dafür sorgen, dass beide dauerhaft zusammenpassen. Eine Ableitung benötigt diese Synchronisation nicht.

Wie sich Query State und Derived State in der Praxis voneinander unterscheiden lassen und warum Filter und Sortierung dabei selbst zum State gehören können, beschreibt Reaktive Filter und Sortierung ausführlicher.
Selektion kann Teil des Zustands sein
Abschnitt betitelt „Selektion kann Teil des Zustands sein“Auch eine Auswahl muss nicht bedeuten, dass eine komplette Entity ein zweites Mal gespeichert wird.
entities+selectedId ↓selectedEntityWenn die Auswahl für den fachlichen oder anwendungsweiten Zustand relevant ist, kann die ID als Source State genügen. Die ausgewählte Entity entsteht anschließend als Ableitung.
Ob eine Auswahl überhaupt in +State gehört, hängt dabei von ihrer Bedeutung ab. Ein kurzzeitig hervorgehobener Tabellen-Eintrag kann reine Presentation sein. Eine Auswahl, auf der weitere fachliche Operationen aufbauen, hat eine andere Lebensdauer und Verantwortung.
Wie ein solcher Select-Flow konkret geschnitten werden kann, zeigt Select by ID.
Mehrere Quellen können zu einer Projektion werden
Abschnitt betitelt „Mehrere Quellen können zu einer Projektion werden“Ableitungen müssen nicht auf eine einzelne Source beschränkt bleiben.
articles+authors+categories ↓ProjektionMehrere State-Sources können gemeinsam eine konsumierbare Projektion ergeben.
Entscheidend ist dabei weniger, wie viele Stores oder Quellen beteiligt sind, sondern wo die Verantwortung für die entstehende Information liegt.
Wie mehrere reaktive Quellen zusammenspielen können, zeigt ViewModel aus mehreren Datenquellen. Projektionen, die über Ereignisse aufgebaut und aktualisiert werden, behandelt Event-driven Projection.
Beide Varianten zeigen denselben grundsätzlichen Gedanken: Consumer müssen nicht zwangsläufig die Rohstruktur des zugrunde liegenden States kennen.
Nicht alles gehört in einen Store
Abschnitt betitelt „Nicht alles gehört in einen Store“Aus der bisherigen Argumentation sollte nicht folgen, dass ein Feature genau einen großen +State besitzen muss.
Store-Grenzen entstehen nicht primär durch Dateigröße oder dadurch, dass mehrere Zustände zufällig denselben Entity-Typ verwenden.
Relevanter sind Fragen wie:
- Welche Verantwortung gehört zusammen?
- Haben die Zustände denselben Lebenszyklus?
- Ändern sie sich aus denselben Gründen?
- Wer besitzt sie?
- Welche Consumer verwenden sie?
Zwei Zustände können denselben Customer verwenden und trotzdem unterschiedliche Verantwortungen haben.
Umgekehrt können mehrere Datentypen gemeinsam zu genau einem fachlichen Zustand gehören.
Wenn der Read Store zu groß wird behandelt diese Grenzen ausführlich.
Für +State ist vor allem wichtig: Der Layer beschreibt eine Art von Verantwortung, nicht die Forderung nach einem einzigen Store.
Fachliche Änderungen und Intents
Abschnitt betitelt „Fachliche Änderungen und Intents“+State besitzt nicht nur lesbaren Zustand. Hier treffen auch fachliche Änderungsabsichten auf den aktuellen Zustand.
deleteRequested ↓aktueller Zustand ↓zulässig / nicht zulässig ↓ZustandsänderungOder:
completeOrder ↓Business Rule ↓State TransitionEin Intent beschreibt zunächst eine Absicht.
„Dieser Kunde soll gelöscht werden“ ist etwas anderes als „Dieser Kunde wurde gelöscht“.
Ob die gewünschte Änderung zulässig ist und welcher fachliche Zustand daraus entsteht, hängt von den Regeln und dem aktuellen Zustand ab.
Wie sich Create-, Retrieve-, Update- und Delete-Flows entlang solcher Verantwortungen schneiden lassen, beschreibt die CRUD-Slices-Serie ausführlich.
Die wichtige Grenze für diesen Artikel ist einfacher: Eine fachliche Änderungsabsicht sollte nicht erst in einer Component oder einem HTTP-Service ihre Bedeutung bekommen.
Wo ViewModels ins Spiel kommen
Abschnitt betitelt „Wo ViewModels ins Spiel kommen“+State kann Zustand und Ableitungen liefern, aus denen später ein ViewModel entsteht.
Dabei muss nicht jede View-Projektion bereits vollständig im Store gebaut werden.
Eine Projektion kann fachlichen Zustand konsumierbar machen. Die Application-Schicht kann anschließend mehrere solcher Informationen für eine konkrete View zusammenführen oder ihr eine passend zugeschnittene API bereitstellen.
Wo diese Grenze sinnvoll liegt, hängt vom jeweiligen Schnitt ab.
ViewModel Aggregation behandelt ausführlicher, wie ViewModels aus mehreren Datenquellen entstehen können, ohne Backend-Strukturen oder interne State-Details bis in die Presentation durchzureichen.
Für +State bleibt entscheidend, dass fachliche Regeln und fachlich relevante Ableitungen nicht erst beim Bau einer konkreten Oberfläche entstehen.
Was +State darf – und was nicht
Abschnitt betitelt „Was +State darf – und was nicht“+State darf und soll beispielsweise:
- fachlich relevanten Laufzeitzustand besitzen
- Business Rules anwenden
- fachliche Zustandsübergänge modellieren
- fachliche Intents verarbeiten
- Derived State erzeugen
- relevante Selektionen bereitstellen
- fachliche Filterungen und Projektionen erzeugen
- auf fachlich relevante Ereignisse reagieren
- Regeln zentralisieren, die unabhängig von der konkreten Presentation gelten
+State sollte nicht:
- HTTP-Requests selbst implementieren
- DTOs parsen
- fremde API-Contracts durch die Fachlichkeit reichen
HttpClient,fetchoder SDK-spezifische APIs bedienen- technische API-Fehlercodes interpretieren
- den Router direkt steuern
- Dialoge öffnen
- Snackbars oder Toasts anzeigen
- DOM- oder UI-Framework-Details kennen
- zwischen Angular Material, Ionic oder Bootstrap unterscheiden
- konkrete View-Orchestrierung übernehmen, die zur Application gehört
Bei Grenzfällen hilft eine einfache Frage:
Würde diese Regel auch dann noch gelten, wenn Material morgen durch Ionic ersetzt wird?
Wenn ja, ist sie ein guter Kandidat für +State.
Das ist keine Definition, aber eine erstaunlich brauchbare Leitlinie.
Eine Fachlichkeit, mehrere Oberflächen
Abschnitt betitelt „Eine Fachlichkeit, mehrere Oberflächen“Die Vorteile dieses Schnitts werden besonders sichtbar, wenn mehrere Oberflächen dieselbe Fachlichkeit verwenden.
In einem Langzeitprojekt wurden Angular Material und Ionic über Jahre parallel auf derselben fachlichen Basis betrieben.
Das funktionierte, weil Business Rules nicht in beiden Presentations separat implementiert wurden.
+State / \ / \ Material IonicDie Presentation entscheidet über Darstellung. +State stellt die fachlichen Aussagen bereit.
Wäre canOrder in beiden Presentations separat berechnet worden, hätten zwei Implementierungen dauerhaft synchron gehalten werden müssen. Früher oder später hätten beide Oberflächen unterschiedliche Antworten auf dieselbe fachliche Frage liefern können.

Aus derselben Trennung entstehen weitere Vorteile.
Business Rules werden auffindbar. Eine fachliche Entscheidung, die über Components, Pipes, Form Validators, Services und Templates verteilt ist, lässt sich nicht mehr an einer Stelle nachvollziehen. Man muss sie rekonstruieren.
Wer weiß, dass canOrder im fachlichen State entsteht, weiß auch, wo er nach der Regel suchen muss.
Ableitungen bleiben konsistent, weil nicht mehrere Kopien derselben Information manuell synchronisiert werden müssen.
Und Business Rules lassen sich unabhängig von einer konkreten Oberfläche testen. Ob Material einen Button rendert oder Ionic einen Menüpunkt, ist für die Entscheidung canOrder irrelevant.
Diese Vorteile entstehen nicht durch die verwendete State-Library. Sie entstehen durch die Grenze.
Nicht jeder Zustand ist +State
Abschnitt betitelt „Nicht jeder Zustand ist +State“Eine klare Heimat für Fachlichkeit bedeutet nicht, jede Variable in einen Store zu verschieben.
Dinge wie:
isDialogOpenhoveredRowactiveTabtooltipVisibleaccordionExpandedsind häufig Presentation State. Sie beschreiben, wie eine konkrete Oberfläche gerade dargestellt wird.
Auch diese Grenze ist nicht in jedem Fall absolut. Ein activeTab kann beispielsweise irgendwann eine fachlich relevante Auswahl repräsentieren. Der Name der Variable entscheidet darüber wenig.
Hilfreicher ist eine andere Frage:
Hat dieser Zustand Bedeutung unabhängig von seiner aktuellen Darstellung?
Wenn die Antwort nein lautet, ist +State wahrscheinlich nicht sein natürlicher Ort.
Die Grenze zur Application
Abschnitt betitelt „Die Grenze zur Application“+State besitzt fachlich relevanten Zustand, Business Rules, Ableitungen, Selektionen, Zustandsübergänge und fachliche Intents.
Eine konkrete View sollte deshalb trotzdem nicht den gesamten State kennen müssen.
Eine Customer-List-View benötigt vielleicht nur:
customersloadingselectCustomer()createCustomer()Eine Detail-View benötigt einen anderen Ausschnitt derselben Fachlichkeit.
Damit entsteht die nächste Verantwortungsgrenze: +State besitzt und interpretiert die Fachlichkeit. Die Application schneidet daraus eine API zu, die zu einem konkreten Anwendungsfall oder einer konkreten View passt.
Die Fachlichkeit bleibt dadurch an einem Ort, ohne dass jede Presentation ihre gesamte innere Struktur kennen muss.