Signals machen schlechte Architektur nicht besser
Signals sind gut.
Wirklich.
Feingranulare Reaktivität, klareres Dependency-Tracking, weniger indirekte Subscription-Mechanik, bessere lokale Ableitbarkeit von Zustand — das sind echte Verbesserungen.
Aber Signals sind ein Reaktivitätsprimitive.
Keine Architektur.
Und genau dort beginnt das Missverständnis.
Ein neues Reaktivitätsmodell löst keine falschen Verantwortlichkeiten. Es verhindert keine DTO-Leakage. Es zieht keine Schichtgrenzen. Es entscheidet nicht, wem Zustand gehört. Es sorgt nicht automatisch dafür, dass Komponenten klein bleiben.
Wer vorher schlechte Struktur hatte, kann mit Signals sehr schnell reagierenden schlechten Code bauen.

Das Missverständnis
Abschnitt betitelt „Das Missverständnis“Neue Framework-Features erzeugen oft eine kurze Phase kollektiver Hoffnung.
Endlich wird alles einfacher.
Endlich weniger Boilerplate.
Endlich keine Subscription-Hölle mehr.
Endlich kein RxJS für jeden kleinen UI-Zustand.
Und ja: vieles davon stimmt.
Signals können Code deutlich vereinfachen. Sie können lokale Reaktivität verständlicher machen. Sie können Ableitungen näher an den Zustand bringen. Sie können verhindern, dass man für triviale UI-Zustände ein halbes Observable-Theater aufführt.
Aber sie beantworten keine Architekturfragen.
Sie beantworten nicht:
- Wo lebt fachlicher Zustand?
- Wer darf ihn ändern?
- Wo endet der UI-Layer?
- Wo werden DTOs übersetzt?
- Wo werden Use-Cases orchestriert?
- Wie reagieren mehrere Teile der Anwendung auf ein fachliches Ereignis?
- Welche Schicht kennt Infrastruktur?
- Welche Schicht kennt Darstellung?
Signals geben dir einen sehr guten Draht.
Sie sagen dir nicht, wo du ihn verlegen sollst.
Was Signals nicht lösen
Abschnitt betitelt „Was Signals nicht lösen“DTO-Leakage
Abschnitt betitelt „DTO-Leakage“Wenn eine API-Struktur direkt in ein Signal geschrieben wird, ist sie immer noch im falschen Layer.
type TaskDto = { task_id: string; created_at: string; assigned_to_user_id: string | null;};
@Component(...)export class TaskCardComponent { readonly task = signal<TaskDto | null>(null);}Das ist nicht besser, nur weil task jetzt ein Signal ist.
Die Komponente kennt weiterhin API-Feldnamen.
Das Template kennt weiterhin Transportdetails.
created_at ist weiterhin ein String.
assigned_to_user_id: null muss weiterhin irgendwo fachlich interpretiert werden.
Signals machen daraus keine saubere Grenze.
Sie machen nur die falsche Grenze reaktiv.
Besser ist weiterhin:
type TaskViewModel = { id: string; createdAtLabel: string; assigneeLabel: string;};
function toTaskViewModel(dto: TaskDto): TaskViewModel { return { id: dto.task_id, createdAtLabel: formatDate(dto.created_at), assigneeLabel: dto.assigned_to_user_id ?? 'Unzugewiesen', };}Die UI sollte ein ViewModel konsumieren.
Nicht das Transportmodell der API.
Business-Logik in Komponenten
Abschnitt betitelt „Business-Logik in Komponenten“Ein Signal in einer Komponente ist nicht automatisch schlechte Architektur.
Lokaler UI-State ist dort völlig okay.
@Component(...)export class OrderDialogComponent { readonly open = signal(false);
show() { this.open.set(true); }
close() { this.open.set(false); }}Das ist UI-Verhalten.
Problematisch wird es, wenn die Komponente fachliche Abläufe besitzt.
@Component(...)export class OrderComponent { readonly loading = signal(false); readonly error = signal<string | null>(null);
constructor( private readonly http: HttpClient, private readonly router: Router, private readonly toast: ToastService, ) {}
submitOrder(data: OrderData) { if (data.items.length === 0) { this.error.set('Bestellung ohne Positionen ist nicht erlaubt'); return; }
const payload = this.mapToPayload(data);
this.loading.set(true);
this.http.post('/api/orders', payload).subscribe({ next: () => { this.toast.success('Bestellung gespeichert'); this.router.navigate(['/confirmation']); }, error: () => { this.error.set('Speichern fehlgeschlagen'); }, complete: () => { this.loading.set(false); }, }); }}Das Problem ist nicht signal().
Das Problem ist, dass die Komponente plötzlich alles besitzt:
- fachliche Regel
- Mapping
- HTTP
- Loading-State
- Error-State
- Toast
- Navigation
- Ablaufsteuerung
Das war vorher mit Observables schlecht.
Mit Signals ist es immer noch schlecht.
Nur etwas moderner geschrieben.

Fehlende Layertrennung
Abschnitt betitelt „Fehlende Layertrennung“Signals können überall auftauchen:
In Komponenten. In Services. In Stores. In Facades. In Mappers. In irgendwelchen Utility-Klassen, in denen sie besser nicht wären.
Das macht sie flexibel.
Und genau deshalb gefährlich.
Wenn ein Team kein klares Ownership-Modell für Zustand hat, entstehen schnell Fragen wie:
- Welches Signal ist die Quelle der Wahrheit?
- Wer darf
.set()aufrufen? - Ist das Signal lokal oder fachlich?
- Ist es UI-State oder Application-State?
- Warum wird derselbe Zustand an drei Stellen gespiegelt?
- Warum hat ein Service ein Signal, eine Komponente ein Signal und der Store auch?
Dann hat man keine Architektur.
Man hat verteilte Reaktivität.
@Injectable()export class OrderService { readonly selectedOrderId = signal<string | null>(null);}
@Component(...)export class OrderListComponent { readonly selectedOrderId = signal<string | null>(null);}
@Injectable()export class OrderFacade { readonly selectedOrderId = signal<string | null>(null);}Das ist nicht automatisch falsch.
Aber ohne klare Regel ist es verdächtig.
Denn jetzt existiert dieselbe fachliche Idee an mehreren Orten.
Und irgendwann fragt jemand:
Warum ist die Detailansicht auf Order 42, aber die Liste markiert Order 17?
Die Antwort lautet meistens nicht: „Signals sind schlecht.“
Die Antwort lautet:
Niemand hat entschieden, wem dieser Zustand gehört.
Signals ohne Ownership sind globale Variablen mit Extra-Schritten
Abschnitt betitelt „Signals ohne Ownership sind globale Variablen mit Extra-Schritten“Ein Signal ist verführerisch einfach.
export const currentUser = signal<User | null>(null);Fertig.
Überall importieren. Überall lesen. Überall schreiben.
Was soll schon passieren?
Eine Menge.
Denn damit ist currentUser zwar reaktiv, aber nicht architektonisch kontrolliert.
Wer darf den User setzen? Wann wird er zurückgesetzt? Was passiert beim Logout? Was passiert bei Token Refresh? Was passiert bei Mandantenwechsel? Welche Tests müssen das vorbereiten? Welche Komponente hängt indirekt daran?
Ein globales Signal ohne Ownership ist keine State-Management-Lösung.
Es ist globaler Zustand.
Mit hübscherem API.
Was Signals ermöglichen
Abschnitt betitelt „Was Signals ermöglichen“Signals sind dann stark, wenn sie innerhalb klarer Grenzen eingesetzt werden.
Zum Beispiel:
Component konsumiert ViewModel sendet Intent
Facade / Store besitzt Zustand koordiniert UI-nahe Flüsse bietet abgeleitete Signals an
Use Case / Application Service führt fachliche Abläufe aus
Infrastructure spricht mit API, Storage, externen SystemenDann passen Signals sehr gut.
@Injectable()export class OrderFacade { private readonly store = inject(OrderStore);
readonly vm = this.store.vm; readonly isLoading = this.store.isLoading;
loadOrders() { this.store.loadOrders(); }
submitOrder(data: OrderFormValue) { this.store.submitOrder(data); }}Die Komponente bleibt langweilig:
@Component(...)export class OrderComponent { private readonly facade = inject(OrderFacade);
readonly vm = this.facade.vm;
submit(data: OrderFormValue) { this.facade.submitOrder(data); }}Die Komponente weiß nicht, was danach passiert.
Sie weiß nicht, ob ein HTTP-Call ausgeführt wird. Sie weiß nicht, ob ein Toast erscheint. Sie weiß nicht, ob navigiert wird. Sie weiß nicht, ob eine Liste neu geladen wird.
Sie sendet einen Intent.
Das ist ihre Aufgabe.

Signal Store ist kein Freifahrtschein
Abschnitt betitelt „Signal Store ist kein Freifahrtschein“Ein Signal Store kann eine sehr gute Application Facade sein.
Aber auch ein Store kann schlecht geschnitten sein.
Wenn alles in einem Store landet, entsteht nur eine neue Datenhalde:
type AppState = { user: User | null; orders: Order[]; selectedOrderId: string | null; dialogOpen: boolean; formDraft: OrderFormValue | null; permissions: string[]; theme: string; toastMessage: string | null; tempFlag: boolean;};Das ist nicht besser, nur weil es Signal Store ist.
Ein Store braucht Grenzen.
Besser:
AuthStore user permissions
OrdersStore orders selectedOrderId loading error commands
OrderDialogStore local dialog state draft stateNicht jeder Zustand gehört in denselben Ort.
Signals machen Zustand leicht.
Gerade deshalb muss man entscheiden, welcher Zustand wohin gehört.
Computed ist keine Domain-Schicht
Abschnitt betitelt „Computed ist keine Domain-Schicht“computed() ist großartig für Ableitungen.
readonly fullName = computed(() => { const user = this.user();
if (!user) { return 'Unbekannt'; }
return `${user.firstName} ${user.lastName}`;});Das ist harmlos.
Aber auch hier gibt es eine Grenze.
Wenn in computed() plötzlich komplexe fachliche Regeln landen, ist Vorsicht angebracht.
readonly discount = computed(() => { const order = this.order();
if (!order) { return 0; }
if (order.customer.type === 'premium' && order.items.length > 10) { return 0.15; }
if (order.customer.country === 'DE' && order.total > 1000) { return 0.1; }
return 0;});Vielleicht ist das okay.
Vielleicht ist das aber eine fachliche Regel, die nicht in einer UI-nahen Ableitung wohnen sollte.
Die Frage lautet nicht:
Darf ich computed verwenden?
Natürlich.
Die Frage lautet:
Ist das eine Darstellungableitung oder eine fachliche Entscheidung?
Darstellungableitungen passen gut in Store, Facade oder ViewModel-Mapping.
Fachliche Entscheidungen gehören in einen Use Case, eine Domain-Funktion oder eine Application-Schicht.
Effects sind kein Mülleimer für Seiteneffekte
Abschnitt betitelt „Effects sind kein Mülleimer für Seiteneffekte“Auch effect() ist kein Architekturkonzept.
Ein Effect kann sinnvoll sein, um auf Zustandsänderungen zu reagieren.
Aber wenn Effects wahllos über Komponenten und Services verteilt werden, entsteht wieder unsichtbare Kopplung.
effect(() => { if (this.saved()) { this.toast.success('Gespeichert'); this.router.navigate(['/orders']); this.reload(); }});Das kann funktionieren.
Aber man sollte sehr genau wissen, warum dieser Effect an dieser Stelle sitzt.
Denn Effects sind Reaktionen.
Reaktionen brauchen Ownership.
Wer besitzt den Save-Flow?
Wer entscheidet über Navigation?
Wer entscheidet über Toasts?
Wer lädt welche Ressource neu?
Wie testet man das?
Was passiert, wenn eine zweite Stelle ebenfalls auf saved() reagiert?
Ohne Konzept wird aus effect() schnell:
Irgendwo passiert dann halt noch etwas.
Das ist keine Architektur.
Das ist reaktiver Nebel.
Gute Signals-Architektur ist langweilig
Abschnitt betitelt „Gute Signals-Architektur ist langweilig“Eine gute Signals-Architektur ist nicht spektakulär.
Sie sieht ungefähr so aus:
DTO reinMapper übersetztStore hält Zustandcomputed erzeugt ViewModelComponent rendertIntent geht rausUse Case läuftResult aktualisiert ZustandDas ist nicht neu.
Das ist der Punkt.
Signals ändern nicht die Grundprinzipien guter Frontend-Architektur.
Sie machen nur manche Implementierungen angenehmer.
Der Unterschied liegt nicht darin, ob man signal(), Observable, Subject, Redux oder einen Signal Store verwendet.
Der Unterschied liegt darin, ob Zuständigkeiten klar sind.
Eine praktische Regel
Abschnitt betitelt „Eine praktische Regel“Signals sind gut geeignet für:
- lokalen UI-State
- abgeleiteten Zustand
- ViewModels
- Feature Stores
- Facades
- lesbare reaktive Datenflüsse
- feingranulare Updates
Signals lösen aber nicht automatisch:
- Layertrennung
- DTO-Mapping
- Use-Case-Orchestrierung
- Ownership von Zustand
- saubere Event-Flüsse
- fachliche Modellierung
- Testbarkeit
- Komponentenverantwortung
Die technische Frage lautet:
Kann ich das mit Signals bauen?
Die architektonische Frage lautet:
Sollte dieser Zustand oder diese Entscheidung hier wohnen?
Die zweite Frage ist wichtiger.
Signals sind ein gutes Werkzeug.
Aber sie sind kein Ersatz für Architektur.
Sie verbessern die Implementierung guter Strukturen. Sie retten keine schlechten Strukturen.
Wer DTOs direkt in Komponenten schiebt, hat mit Signals immer noch DTO-Leakage.
Wer Use-Cases in Komponenten schreibt, hat mit Signals immer noch Business-Logik im UI-Layer.
Wer Zustand ohne Ownership verteilt, hat mit Signals immer noch unkontrollierten Zustand.
Wer Seiteneffekte wahllos in Effects versteckt, hat mit Signals immer noch schwer nachvollziehbare Flüsse.
Signals machen reaktive Programmierung in Angular angenehmer.
Aber sie beantworten nicht die alte, unbequeme Frage:
Wer ist wofür verantwortlich?
Die kurze Regel:
Signals machen guten Code besser lesbar. Schlechte Architektur machen sie nur schneller sichtbar.
Oder etwas gemeiner:
Wer vorher Spaghetti gebaut hat, bekommt mit Signals keine Lasagne. Nur feinere Nudeln.