Infrastructure – die technische Außenwelt
Infrastructure hat einen schlechten Ruf.
Sobald das Wort fällt, denken viele an Ports, Adapter, Repository-Interfaces, Factories und mehrschichtige Integrationsarchitekturen. An Diagramme mit vielen Pfeilen und noch mehr Rechtecken.
Der Einleitungsartikel dieser Serie beschreibt Infrastructure als die erste Station auf dem Weg von der technischen Außenwelt zum Nutzer. Was an dieser ersten Station tatsächlich passiert, ist deutlich unspektakulärer, als der Name vermuten lässt.
Für diesen Artikel reicht zunächst eine einfache Sicht:
Infrastructure ist die Stelle, an der unsere Anwendung auf etwas trifft, das sie nicht vollständig kontrolliert.
Ein Backend. Eine REST- oder GraphQL-API. WebSocket oder SSE. Local Storage. IndexedDB. Eine Browser-API. Ein Authentication Provider. Ein externes SDK. Ein anderer Bounded Context.
„Nicht vollständig kontrolliert“ heißt dabei nicht zwangsläufig „fremde Firma“. Auch das Backend-Team im eigenen Unternehmen kann einen Contract ändern, ohne dass das Frontend diese Änderung verhindern kann. Entscheidend ist weniger die Organisationsgrenze als die Frage, wem eine Struktur gehört und wer über ihre Weiterentwicklung entscheidet.
Die konkrete Technologie ist zweitrangig. Ob Angular HttpClient, fetch, Axios oder ein anderer Client verwendet wird, ändert an der architektonischen Verantwortung nichts.
Außenwelt ↓Infrastructure ↓eigene FachlichkeitInfrastructure nimmt fremde technische Strukturen entgegen und übersetzt sie kontrolliert in die eigene Welt.
Mehr muss sie zunächst nicht leisten.

Und genau das ist der Punkt: Infrastructure darf langweilig sein.
Die Außenwelt gehört uns nicht
Abschnitt betitelt „Die Außenwelt gehört uns nicht“Ein Contract eines Backends gehört zunächst dem Backend. Ein externes SDK gehört einem anderen Hersteller. Ein Browser-API-Contract gehört dem Browser.
Diese Modelle sollten nicht automatisch zu unserem eigenen Domain Model werden, nur weil sie zuerst bei uns ankommen.
Ein kleines Beispiel genügt. Ein Backend liefert einen Kunden:
type CustomerDto = { id: string; first_name: string; last_name: string;};Intern arbeiten wir mit:
type Customer = { id: string; firstName: string; lastName: string;};Das ist keine anspruchsvolle Transformation. Gerade die Banalität des Beispiels ist wichtig, denn viele Grenzen im Alltag sehen genauso unspektakulär aus.
CustomerDto beschreibt, wie das Backend einen Kunden liefert. Customer beschreibt, wie unsere Anwendung über einen Kunden spricht. Beide können sich unabhängig voneinander entwickeln, solange dazwischen eine definierte Übersetzung existiert.
Aber das ist doch nur ein Mapper
Abschnitt betitelt „Aber das ist doch nur ein Mapper“An dieser Stelle liegt ein naheliegender Einwand:
function toCustomer(dto: CustomerDto): Customer { return { id: dto.id, firstName: dto.first_name, lastName: dto.last_name, };}Wozu zusätzlicher Code, wenn beide Strukturen sich kaum unterscheiden?
Heute sieht der Mapper tatsächlich beinahe unnötig aus. Morgen ändert das Backend vielleicht seine Feldnamen:
first_name → givenNamelast_name → familyNameDann ändert sich genau diese Übersetzung. Store, Business Rules, Facades und Components müssen davon idealerweise nichts mitbekommen.

Ein Mapper mit fünf Zeilen ist kein Overengineering, wenn er verhindert, dass ein fremder Contract in fünfzig Dateien zum heimlichen Domain Model wird.
Was passiert, wenn DTOs diese Grenze ungehindert überschreiten und sich durch State, Application, Components und Templates ziehen, behandelt der Denkzettel zu DTO-Leakage ausführlicher.
Für diesen Artikel reicht die Erkenntnis: Eine kleine Übersetzungsgrenze kann verhindern, dass die Struktur eines fremden Systems schleichend zur Struktur der eigenen Anwendung wird.
TypeScript schützt die Grenze nicht
Abschnitt betitelt „TypeScript schützt die Grenze nicht“Ein weiterer Punkt wird im Frontend leicht übersehen.
Ein HTTP Response stammt aus einer externen Quelle. Nach dem JSON-Parsing haben wir JavaScript-Werte: Strings, Zahlen, Objekte, Arrays, null.
Eine Type Assertion wie:
const customer = response as CustomerDto;oder ein typisierter HTTP-Aufruf wie:
http.get<CustomerDto>('/api/customers/42');beschreibt die Erwartung des Frontends. Sie beweist nicht, dass das Backend tatsächlich genau diese Struktur geliefert hat.
Ein TypeScript-Typ validiert keine externen Daten. Er beschreibt zur Compile-Zeit, mit welcher Struktur wir arbeiten wollen. Ob die Daten dieser Struktur zur Laufzeit entsprechen, muss an der Systemgrenze bei Bedarf separat geprüft werden.
Diese Prüfung gehört zur Infrastructure.
Prüfen, normalisieren, übersetzen
Abschnitt betitelt „Prüfen, normalisieren, übersetzen“Der Job der Grenze lässt sich auf wenige Aufgaben reduzieren:
unknown / externe Daten ↓ Infrastructure │ ├── prüfen ├── normalisieren └── übersetzen ↓ eigenes ModellTypische Aufgaben sind:
- Runtime-Validierung
- erforderliche Felder prüfen
- externe Formate normalisieren
- Datumswerte konvertieren
nullundundefinedkontrolliert behandeln- technische Statuscodes übersetzen
- DTOs in eigene Modelle transformieren
- Fehler fremder Systeme in eigene technische Fehler übersetzen
Für Runtime-Validierung können Libraries wie Zod verwendet werden. Welche konkrete Library diese Aufgabe übernimmt, ist für die Architekturentscheidung zweitrangig. Entscheidend ist die Stelle, an der die Prüfung stattfindet.
Auch Normalisierung sollte dabei langweilig und eindeutig bleiben.
Ein bekanntes Datumsformat kann in ein Date überführt werden. Ein optionales Feld kann nach einer festgelegten technischen Regel normalisiert werden. Ein externer Statuscode kann auf eine eigene technische oder fachliche Repräsentation abgebildet werden.
Sobald eine Abweichung nicht mehr eindeutig technisch interpretiert werden kann, sollte Infrastructure daraus keine Fachlichkeit erfinden.
Nicht interpretierbare Daten sind zunächst ein technischer Fehler.
Wie die Anwendung diesen Fehler gegenüber dem Nutzer darstellt, ist bereits eine andere Verantwortung.
Nicht nur DTOs
Abschnitt betitelt „Nicht nur DTOs“Das Customer-Beispiel verwendet HTTP, weil sich daran die Idee besonders leicht zeigen lässt. Dieselbe Grenze existiert überall dort, wo Daten von außen kommen.
Ein Wert aus localStorage ist zunächst ein String oder null. Er ist nicht automatisch das Objekt, das gestern dort gespeichert wurde.
Ein SDK eines Zahlungsanbieters wirft eigene Fehlerklassen mit eigenen Codes.
Ein WebSocket liefert Nachrichten, deren Struktur sich mit einer neuen Serverversion ändern kann.
In allen Fällen stellt sich dieselbe Frage: Übernehmen wir den Rohwert in unsere Anwendung oder übersetzen wir ihn zunächst in etwas, das unser Feature kennt?
Auch Fehler können Teil dieser Übersetzung sein:
try { await paymentSdk.charge(order);} catch (error) { throw toPaymentInfrastructureError(error);}toPaymentInfrastructureError darf die Fehlercodes und technischen Besonderheiten des SDKs kennen. Der Rest der Anwendung arbeitet anschließend mit einem eigenen, kontrollierten Satz technischer Fehlerfälle.
Wechselt der Zahlungsanbieter, bleibt diese Fremdsprache an der Infrastructure-Grenze.
Verteidigung ist keine fachliche Entscheidung
Abschnitt betitelt „Verteidigung ist keine fachliche Entscheidung“Hier liegt die wichtigste Grenze dieses Artikels.
Infrastructure darf externe Unsicherheit beseitigen. Sie soll daraus keine Business Rules ableiten.
Ein externer Contract liefert beispielsweise:
"ACTIVE" ↓CustomerStatus.ActiveDiese Übersetzung kann Aufgabe der Infrastructure sein.
Ob ein aktiver Kunde eine Bestellung aufgeben darf, ist eine andere Frage:
CustomerStatus.Active ↓Darf dieser Kunde bestellen?Hier beginnt die Fachlichkeit.
Beide Operationen können im Code sehr ähnlich aussehen. Ein switch, ein if, vielleicht nur eine einzelne Zuweisung. Ihre Verantwortung ist trotzdem grundverschieden.
Infrastructure kann beispielsweise übersetzen:
"2026-08-10T10:15:00Z" → Datenull → undefined"C" → CreditHoldfirst_name → firstName+State beantwortet dagegen Fragen wie:
Darf der Auftrag storniert werden?Kann der Kunde bestellen?Ist dieser Zustandsübergang zulässig?Welche Aktionen stehen fachlich zur Verfügung?Infrastructure beseitigt technische und strukturelle Unsicherheit. Fachliche Entscheidungen beginnen danach.
Was Infrastructure darf – und was nicht
Abschnitt betitelt „Was Infrastructure darf – und was nicht“Infrastructure darf und soll:
- externe Contracts kapseln
- DTOs in eigene Modelle übersetzen
- technische Formate normalisieren
- externe Eingaben validieren
- technische APIs kapseln
- fremde Fehler in eigene technische Fehler übersetzen
- verhindern, dass fremde Modelle unkontrolliert in die Anwendung durchsickern
Infrastructure sollte nicht:
- Business Rules implementieren
- fachliche Entscheidungen treffen
- fachliche Ableitungen erzeugen
- ViewModels für konkrete Views bauen
- UI-Zustand verwalten
- Navigation steuern
- Dialoge öffnen
- Presentation-Verantwortung übernehmen
- Logik aufnehmen, nur weil man sich ohnehin gerade in einem technischen Service befindet
Je interessanter die Businesslogik im Infrastructure Layer wird, desto genauer lohnt sich der Blick darauf, ob sie dort wirklich hingehört.
Wann daraus mehr wird
Abschnitt betitelt „Wann daraus mehr wird“Der triviale Mapper aus dem Customer-Beispiel ist noch kein vollständiger Anti-Corruption Layer im engeren Sinne, wie ihn Eric Evans im Kontext von Domain-Driven Design beschreibt.
Beide teilen jedoch eine defensive Grundidee: Ein fremdes Modell soll nicht unkontrolliert zum eigenen Modell werden.
Solange lediglich first_name zu firstName wird, bleibt diese Grenze ausgesprochen unspektakulär.
Interessanter wird sie, sobald unterschiedliche Begriffe, Statusmodelle oder fachliche Bedeutungen aufeinandertreffen. Vielleicht bedeutet "ACTIVE" im fremden System etwas anderes als Active in unserem Feature. Vielleicht transportiert ein Legacy-System Fehlercodes, deren Bedeutung nur dort existiert. Vielleicht modellieren beide Systeme denselben Kunden, verstehen darunter aber fachlich etwas anderes.
Dann reicht mechanisches Feld-Mapping irgendwann nicht mehr.
Genau diese weitergehende Übersetzung zwischen fremdem und eigenem Modell behandelt der Artikel zum Anti-Corruption Layer.
Für den Infrastructure Layer bleibt die wichtige Erkenntnis zunächst einfacher: Schon eine kleine explizite Grenze kann verhindern, dass ein fremdes Modell unbemerkt durch die gesamte Anwendung wandert.
So viel Grenze wie nötig, so wenig Infrastructure wie möglich
Abschnitt betitelt „So viel Grenze wie nötig, so wenig Infrastructure wie möglich“Nicht jedes primitive Feld braucht ein eigenes Value Object. Nicht jedes API-Objekt braucht automatisch ein Interface, ein Repository, einen Port, einen Adapter, eine Factory und mehrere Abstraktionsebenen.
Layering ist kein Wettbewerb um möglichst viele Dateien.
Auch ein trivialer Contract muss nicht zwangsläufig mehrfach modelliert werden. Entscheidend ist das Risiko, das mit seiner direkten Verwendung verbunden ist.
Hilfreiche Fragen sind:
- Wem gehört der Contract?
- Kann er sich unabhängig von uns ändern?
- Wie weit würde er in die Anwendung hineinreichen?
- Gibt es unterschiedliche Semantik zwischen außen und innen?
- Müssen Daten normalisiert oder validiert werden?
- Wie teuer wäre eine spätere Entkopplung?
Eine kleine, ausschließlich lokal verwendete technische Lookup-Struktur braucht möglicherweise keine zusätzliche Abstraktion.
Ein Zahlungsstatus, von dem später fachliche Entscheidungen abhängen, verdient dagegen eine bewusst gesetzte Grenze – selbst wenn seine Übersetzung am Anfang nur aus wenigen Zeilen besteht.
Der Unterschied liegt nicht in der Anzahl der Codezeilen.
Er liegt darin, wie viel Kopplung entsteht, wenn wir auf die Grenze verzichten.
So viel Grenze wie nötig, so wenig Infrastructure wie möglich.
Infrastructure darf langweilig bleiben.
Von hier beginnt +State
Abschnitt betitelt „Von hier beginnt +State“Am Ende des Infrastructure Layers haben wir externe Daten empfangen, geprüft, normalisiert und in eine Form übersetzt, der unser Feature vertrauen kann.
Eine fachliche Entscheidung wurde dabei noch nicht getroffen.
"ACTIVE" wurde zu CustomerStatus.Active. Ob dieser Kunde bestellen darf, ob ein Auftrag storniert werden kann oder welcher Zustandsübergang gerade zulässig ist, steht damit noch nicht fest.
Was mit diesen Daten passiert, sobald sie unsere technische Grenze passiert haben, ist die Frage, mit der +State beginnt.
Dort beginnt die Fachlichkeit.