Zum Inhalt springen

Unabhängiges F&E-Projekt · Köln

TypeScript und Node

Laufzeitschemata als Quelle der Typen, Validierungsvorgaben der Frameworks, AsyncLocalStorage und Abhängigkeitsregeln.

Nicht normativ

Companion-Version
1.0
Bezug zu COADF Core
2.2
Status
Aktuell
Zuletzt geprüft
Profilversion
1.0
Codebeispiele
Codebeispiele folgen in einer späteren Revision.

Codebeispiele folgen in einer späteren Revision.

Behandelte Architektureigenschaften

  • P-1Ein Laufzeitschema als Vertrag, der statische Typ daraus abgeleitet und, im Referenzmuster, eine Factory als einziger Weg zu einem verifizierten Wert.
  • P-4OpenTelemetry-Kontext über AsyncLocalStorage und die Audit-Identität als Pflichtfeld jeder Nachricht und jedes Jobs.
  • P-5Herkunft im Schema deklariert und vom Response-Serialisierer bewahrt.
  • P-6Abhängigkeitsregeln und Schemaprüfungen in der Continuous Integration, jeweils mit einem eingebauten Fehler.
  • P-7Ports im Domänenpaket; ein Adapterpaket pro externem System.
  • P-8Eine Policy-Schnittstelle über einer Engine, per HTTP oder zu WebAssembly kompiliert, mit der Revision an jeder Entscheidung.
  • P-2, P-3Nur in der Tiefe, in der COADF sie veröffentlicht.

Architektonische Absicht

TypeScript gibt der Grenze ein Vokabular und nichts von ihrer Durchsetzung. Typannotationen ändern nie das Laufzeitverhalten eines Programms: Sie beschreiben, was ein Wert sein soll, und verschwinden, bevor er läuft, während die Daten, die von einem Modell, einer Queue oder einer Anfrage ankommen, die Form haben, die sie eben haben. Die Standard-Typdefinitionen deklarieren das Ergebnis von JSON.parse als any, das der Compiler dann als beliebigen Typ behandeln lässt.

In diesem Ökosystem ist die Grenze deshalb das Laufzeitschema, und der statische Typ wird aus dem Schema abgeleitet, nie umgekehrt. Alles Weitere in diesem Profil folgt aus dieser Umkehrung. Zod, Ajv, Fastify und NestJS sind Beispiele: COADF setzt keines davon voraus, und jeder Laufzeitvalidator, der ablehnt, was der Vertrag nicht deklariert, hält die Eigenschaft ein.

Technologiezuordnung

TypeScript und Node: Technologiezuordnung
ArchitektureigenschaftTypeScript und Node
GrenztypEin Laufzeitschema (Zod oder JSON Schema mit Ajv) als Quelle, der statische Typ daraus inferiert. Das Typsystem von TypeScript ist strukturell, deshalb braucht ein verifizierter Wert, der nicht mit einem Vorschlag verwechselt werden darf, ein Branding, und nur eine Factory bringt es an.
Laufzeitvalidierungparse oder safeParse auf jeder empfangenden Seite. parse von Zod wirft bei ungültigen Daten, safeParse gibt stattdessen ein Ergebnis zurück.
Unbekannte SchlüsselEine explizite Wahl pro Schema. In Zod 4 entfernt z.object() unbekannte Schlüssel, und z.strictObject() lehnt sie ab.
Validierung im FrameworkFastify-Routenschemata, mit Ajv validiert und mit fast-json-stringify serialisiert; oder die NestJS-ValidationPipe mit whitelist und forbidNonWhitelisted.
ArchitekturregelAbhängigkeitsregeln, in der CI geprüft: dependency-cruiser, ESLint-Grenzregeln oder TypeScript-Projektreferenzen, die einen unzulässigen Import zu einem Kompilierfehler machen.
AusführungskontextOpenTelemetry JS, dessen Context Manager für Node auf AsyncLocalStorage basiert.
PrüfspurDasselbe PostgreSQL-Schema über einen Treiber; Einträge in der fachlichen Transaktion geschrieben.
Isolierung von StandardsPort-Interfaces im Domänenpaket; jeder Client eines Anbieters auf ein Adapterpaket im Workspace beschränkt.
PolicyOpen Policy Agent per HTTP oder Rego, zu WebAssembly kompiliert und im Prozess über das JavaScript-SDK von OPA ausgewertet; die Revision an jeder Entscheidung.

Referenzmuster

Codebeispiele folgen in einer späteren Revision. Sie werden zurückgestellt, bis eine prüfende Person aus diesem Ökosystem sie gelesen hat, statt geschrieben zu werden, damit die vier Profile gleich aussehen. Die folgende Struktur ist das Referenzmuster, das diese Beispiele umsetzen werden.

Pakete

  • schemas enthält die Laufzeitschemata jedes Vertrags: den Vorschlag mit seiner Herkunft, den Audit-Eintrag mit den veröffentlichten Feldern, die Policy-Entscheidung mit ihrer Revision. Jedes andere Paket importiert seine Typen von hier, aus den Schemata inferiert.
  • inference ruft das Modell auf und gibt das Ergebnis zurück, das beim Parsen der Antwort mit dem Vorschlagsschema entsteht. Es kann records oder audit nicht importieren, und eine Abhängigkeitsregel in der CI hält das fest.
  • records exportiert eine Factory, die einen Vorschlag und die Verifikation durch eine namentlich benannte Person entgegennimmt und einen gebrandeten verifizierten Wert zurückgibt. Der Typ des Brandings ist anderswo ohne Cast nicht konstruierbar, und eine Lint-Regel verbietet diesen Cast außerhalb dieses Pakets.
  • audit, classification mit seinem Anbieteradapter und policy folgen derselben Form wie in den anderen Profilen.

Grenzen zur Laufzeit

Jede empfangende Seite parst: die HTTP-Route, der Message-Consumer, der Job-Runner und die Domänen-Factory. Ein Routenschema in Fastify oder eine Pipe in NestJS deckt nur den Web-Einstiegspunkt ab.

Das OpenTelemetry-SDK wird vor den eigenen Modulen der Anwendung geladen. Eine Anwendung aus ES-Modulen braucht für die automatische Instrumentierung den Loader-Hook von OpenTelemetry, zusätzlich zum Vorladen des SDK.

Fehlermuster

  1. Eine Typannotation mit Validierung verwechselt

    Das Ergebnis von JSON.parse auf den Vorschlagstyp zu casten kompiliert und prüft nichts. Der Cast ist die häufigste Grenze in TypeScript-Code, und er ist keine Grenze.

  2. Entfernen mit Ablehnen verwechselt

    Ein Zod-Objekt mit Standardverhalten entfernt unbekannte Schlüssel. Eine Antwort, die behauptet, verifiziert zu sein, wird nicht abgelehnt; die Behauptung wird stillschweigend entfernt, und mit ihr geht die Tatsache verloren, dass das Modell zu viel behauptet hat. Wo die Behauptung selbst ein Nachweis ist, ein striktes Objekt verwenden und die Ablehnung festhalten.

  3. Typumwandlung als Voreinstellung in Fastify

    Die Standardoptionen von Ajv in Fastify umfassen coerceTypes, useDefaults und removeAdditional. Eine als Text gesendete Zahl kommt als Zahl an, und ein fehlender Wert kann als Standardwert ankommen. Den Validator für Grenzrouten explizit konfigurieren.

  4. Herkunft vom Response-Serialisierer verworfen

    Mit einem Response-Schema werden Properties, die das Schema nicht aufführt, weggelassen, es sei denn, es erlaubt zusätzliche Properties. Ein Response-Schema, das für den Bildschirm geschrieben wurde, ohne die Herkunft, entfernt sie aus jeder Antwort.

  5. Eine NestJS-Pipe, die alles durchlässt

    whitelist entfernt Properties ohne Validierungs-Decorators, und forbidNonWhitelisted wirft, statt zu entfernen. Ohne eines von beiden erreicht eine Property, die das DTO nicht deklariert, den Handler.

  6. Kontext über asynchrone Arbeit hinweg verloren

    AsyncLocalStorage hält Daten für die Lebensdauer einer Anfrage oder einer anderen asynchronen Dauer, und es folgt den Fortsetzungen, die Node sehen kann. Arbeit, die aus einem gepoolten Callback oder der eigenen Queue einer Bibliothek wieder eintritt, kann ohne ihn ankommen; die Audit-Identität muss in der Nachricht stehen und darf nicht aus dem Kontext zurückgewonnen werden.

  7. Automatische Instrumentierung stillschweigend abwesend

    Ein Dienst aus ES-Modulen, der ohne den Loader-Hook gestartet wird, läuft normal, und die Bibliotheken, die der Hook instrumentiert hätte, erzeugen keine Spans. Nichts schlägt fehl; der Trace ist nur kürzer als die Ausführung.

  8. Ein per Cast gefälschtes Branding

    Ein Cast auf den verifizierten Typ kompiliert überall. Branding plus Factory ist eine Konvention, die der Compiler allein nicht durchsetzen kann; erst die Lint-Regel und die Prüfung des Codes machen sie verbindlich.

  9. Zwei Versionen der gemeinsamen Schemata

    Produzent und Konsument hängen von verschiedenen Releases des Pakets schemas ab, und jeder validiert korrekt gegen einen anderen Vertrag. Vertragstests über die tatsächlich deployten Versionen hinweg fangen das ab; Unit-Tests nicht.

Verifikation

  • Unit-Test

    Bestanden, wenn: Fehlerhafte Antworten werden vom Vorschlagsschema abgelehnt: unbekannter Schlüssel, falscher Typ, fehlende Herkunft, unerwartetes Attribut.

    Zahnbeweis: Das strikte Objekt in einem Wegwerf-Branch durch ein Standardobjekt ersetzen: Der Test für den unbekannten Schlüssel schlägt fehl.

  • Unit-Test

    Bestanden, wenn: Ein Test auf Typebene stellt fest, dass ein Vorschlag nicht einem verifizierten Wert zuweisbar ist.

    Zahnbeweis: Das Branding entfernen: Der Test auf Typebene kompiliert nicht mehr.

  • Architekturtest

    Bestanden, wenn: Die Abhängigkeitsregeln bestehen: kein Import aus inference in records oder audit, kein Client eines Anbieters außerhalb seines Adapters.

    Zahnbeweis: Den verbotenen Import einbauen: Die CI schlägt fehl.

  • Vertragstest

    Bestanden, wenn: Die Antwort jeder Route, die einen Vorschlag ausliefert, enthält die Herkunftsfelder.

    Zahnbeweis: Eine Herkunfts-Property aus dem Response-Schema entfernen: Der Test schlägt fehl, weil der Serialisierer sie verworfen hat.

  • Integrationstest

    Bestanden, wenn: Mit einem In-Memory-Span-Exporter erscheint asynchrone Arbeit im Trace der Anfrage, und die persistierten Audit-Einträge tragen eine einzige Identität.

  • Integrationstest

    Bestanden, wenn: Policy-Entscheidungen tragen die Revision des Policy-Materials, das sie erzeugt hat; Antworten ohne Revision werden abgelehnt.

Alternative Umsetzungen

  • Andere Schemabibliotheken (Valibot, TypeBox mit Ajv, io-ts). Die Eigenschaft ist ein Laufzeitschema, das die Quelle des Typs ist; die Bibliothek ist eine Wahl.
  • Schema-first-Transporte wie GraphQL oder Protocol Buffers, bei denen der Vertrag ein eigenes Artefakt ist, aus dem beide Seiten generiert werden.
  • Eine Policy-Engine im Prozess über WebAssembly statt per HTTP, um den Netzwerk-Hop zu entfernen; die Anforderung an die Revision bleibt unverändert.

Abwägungen

  • Schema-first verlagert die maßgebliche Quelle aus den Typdeklarationen heraus, was manche Teams unnatürlich finden; Type-first lässt die Laufzeit ungeschützt.
  • Kompilierte Validatoren sind schnell und bringen Voreinstellungen mit, die umwandeln und auffüllen; die schnellste Konfiguration ist nicht die strengste.
  • Strikte Objekte lehnen mehr von dem ab, was ein Modell erzeugt, und jede Ablehnung verlangt eine Entscheidung darüber, was festgehalten wird.
  • Ein Workspace aus kleinen Paketen macht Abhängigkeitsregeln ausdrückbar und kostet Build- und Release-Maschinerie.

Grenzen

  • Für dieses Profil wird in Companion 1.0 kein Code veröffentlicht, und kein Beispiel wurde ausgeführt. Das beschriebene Verhalten stammt aus der aktuellen Dokumentation der Hersteller, eingesehen am 11. September 2026, für Zod 4, Fastify 5, NestJS, Node.js 24 und OpenTelemetry JS 2.
  • Nichts hier zeigt, wie Konfidenz dargestellt wird, wann eine Prüfung erforderlich ist oder wie die Prüfung organisiert ist; diese Teile von P-2 und P-3 veröffentlicht COADF nicht.

Was dieses Profil nicht begründet

Wer diesem Profil folgt, begründet damit weder die Einhaltung regulatorischer Anforderungen noch eine Zertifizierung oder ein Verfahren zur Bewertung der Übereinstimmung mit Rechtsvorschriften, und weder TypeScript noch Node wird von COADF vorausgesetzt.

Getestete Referenzumgebung

Für dieses Profil wurde kein Beispiel ausgeführt.

Quellen

COADF Engineering Companion 1.0 · nicht normativ · bezieht sich auf COADF Core 2.2

Veröffentlichungsrechte vorbehalten. Für den COADF Engineering Companion 1.0 und seine Referenzbeispiele wird derzeit keine öffentliche Lizenz erteilt.

Schutzrechts- und Veröffentlichungsstatus