Zum Hauptinhalt springen

Imperatives Angular vs. Reaktives Angular – Reifegradmodell

Einordnung

Angular kann imperativ, objektorientiert oder reaktiv genutzt werden.
Die Architekturqualität hängt maßgeblich davon ab, welches Denkmodell dominiert.

Dieses Dokument beschreibt ein Reifegradmodell von:

Imperativem Angular → Reaktivem Angular

Ziel ist nicht Perfektion, sondern Bewusstheit über Architekturentscheidungen.


Überblick – Reifegradstufen

StufeBezeichnungDominantes ParadigmaArchitekturmerkmale
0Scripted AngularImperativLifecycle-gesteuert, lokale Mutation
1Service-Heavy AngularOOP-dominiertLogik in Klassen & Services
2Observable-Aware AngularTeil-reaktivStreams vorhanden, aber entpackt
3Declarative Reactive AngularReaktivDatenfluss bestimmt UI
4Signal-First AngularFunktional-reaktivState-Ableitung statt Mutation

Stufe 0 – Scripted Angular

Merkmale

  • ngOnInit() orchestriert alles
  • viele .subscribe()
  • lokale Zustände
  • manuelle Unsubscribe-Logik
  • imperative Form-Validierung
ngOnInit() {
this.loadUsers();
this.setupForm();
}

Charakteristik

Angular wird wie jQuery mit Klassen verwendet.

UI ist:

ein Objekt, das aktiv Dinge tut.

Risiken

  • Race Conditions
  • Memory Leaks
  • schwer testbar
  • Lifecycle-chaotisch

Stufe 1 – Service-Heavy Angular

Merkmale

  • Logik wird in Services verschoben
  • Komponenten werden dünner
  • aber weiterhin imperatives Denken
  • Basisklassen entstehen
this.userService.load().subscribe(...)

Charakteristik

OOP dominiert.

State liegt in Services als mutable Felder.

Risiken

  • globale Zustände
  • versteckte Seiteneffekte
  • God Services
  • God Base Components

Stufe 2 – Observable-Aware Angular

Merkmale

  • Streams werden genutzt
  • aber häufig sofort entpackt
  • Promise-Konvertierung verbreitet
  • doppelte Zustände
users$ = this.userService.users$;

ngOnInit() {
this.users$.subscribe(u => this.users = u);
}

Charakteristik

Reaktivität ist vorhanden,
aber wird imperativ weiterverarbeitet.

Typisches Symptom

Observable existiert nur formal.

Der eigentliche Code ist weiterhin imperativ.


Stufe 3 – Declarative Reactive Angular

Merkmale

  • async Pipe
  • keine lokalen Spiegelzustände
  • ViewModel-Streams (vm$)
  • combineLatest / map / switchMap
  • keine Lifecycle-Orchestrierung
vm$ = combineLatest([
this.users$,
this.filter$
]).pipe(
map(([users, filter]) => ({
users: users.filter(u => u.active === filter)
}))
);

Template:

<div *ngIf="vm$ | async as vm">
<li *ngFor="let user of vm.users">

Charakteristik

UI ist:

Projektion eines Datenflusses.

Komponenten sind:

  • flach
  • kompositorisch
  • testbar

Stufe 4 – Signal-First Angular

Merkmale

  • Signals statt Subjects
  • computed statt Getter
  • effect nur bei echten Side-Effects
  • kein Lifecycle-orchestriertes Setup
users = signal<User[]>([]);

activeUsers = computed(() =>
this.users().filter(u => u.active)
);

Charakteristik

State ist:

  • explizit
  • synchron ableitbar
  • lokal kontrollierbar

Mutation wird ersetzt durch:

Ableitung.


Vergleich – Imperativ vs. Reaktiv

Imperatives AngularReaktives Angular
Lifecycle steuert LogikDatenfluss steuert UI
subscribe() überallasync Pipe / Signals
Zustand wird gesetztZustand wird abgeleitet
KlassenhierarchieKomposition
MutationImmutability
Seiteneffekte verteiltSeiteneffekte isoliert

Typische Migrationspfade

Von 0 → 2

  • Services einführen
  • Streams statt direkten API-Aufrufen
  • takeUntilDestroyed nutzen

Von 2 → 3

  • keine Spiegelzustände
  • async Pipe konsequent
  • ViewModel-Streams einführen

Von 3 → 4

  • Signals einsetzen
  • computed statt Getter
  • effect nur für echte Side-Effects

Reifegrad-Indikatoren

Niedrige Reife

  • 3 subscribe() pro Komponente

  • Basisklassen für Lifecycle
  • lokale Mutable Arrays
  • Promise-Konvertierung

Hohe Reife

  • keine manuelle Unsubscription
  • keine Spiegelzustände
  • VM-Pattern
  • minimale Lifecycle-Logik
  • klare State-Ableitung

Architekturprinzip

Angular-Komponenten sollen State ableiten, nicht orchestrieren.


Kernaussage

Imperatives Angular funktioniert.
Reaktives Angular skaliert.

Je größer das System, desto höher muss der Reifegrad sein, um Wartbarkeit, Testbarkeit und Stabilität zu sichern.


Schlussgedanke

Reifegrad bedeutet nicht:

„mehr RxJS“.

Sondern:

weniger Kontrolle – mehr Ableitung.

Reaktive Architektur reduziert Komplexität, indem sie expliziten Kontrollfluss durch deklarative Struktur ersetzt.