© whiteMocca/Shutterstock.com, © S&S Media
Teil 4: Die Ergebnisse von MQL-Abfragen im Client typsicher verarbeiten

„Let the Compiler do the dirty Work“


Mit der hierarchischen Abfragesprache (MQL), dem Domänenmodell und dem Schema der letzten Artikel konnten wir komplexe, hierarchische Datenabfragen typsicher formulieren und am Backend gegen MS-SQL-Server mappen. Diesmal generieren wir aus einer MQL-Query und dem Schema das typisierte Datenmodell für den Client.

In den vorherigen Artikeln haben wir TypeScript sowohl als domänenspezifische als auch als Schemabeschreibungssprache und als Grammatik für hierarchische Datenabfragen verwendet. Als letzten Schritt wollen wir in diesem Artikel die vom Backend gelieferten, hierarchischen JSON-Daten am Client typsicher verwenden. Wie immer gilt auch hier: „Let the Compiler do the dirty Work“. Dank der statischen Typisierung verhindern wir zahlreiche Fehler und verbessern die Wartbarkeit der Datenabfragen enorm.

mahringer_typescript_teil4_1.tif_fmt1.jpgAbb. 1: Zusammenfassung der MQL-Abfragegenerierung und -verwendung

Was bisher geschah …

Abbildung 1 enthält den Ablauf der MQL-Idee, den wir zum besseren Verständnis zusammenfassen (die roten Kästen beschreiben die Aufgaben dieses Artikels):

  • Durch Metadaten (Decorators) erweitern wir TypeScript zu einer domänenspezifischen Sprache, mit der wir ein Domänenmodell aus Kunden, Einkäufen und Tickets erstellen.

  • Aus dem Domänenmodell generieren wir automatisiert ein maschinenlesbares Schema.

  • Die Schemainformationen und eine in TypeScript geschriebene Querygrammatik bilden die Grundlage für eine hierarchische Datenabfrage nach Kunden, ihren Einkäufen und den Tickets zu diesen Einkäufen (Listing 1).

  • Die Abfrage mappen wir generisch, aber trotzdem effizient auf eine relationale Datenbank (SQL-Server) und liefern das hierarchische JSON-Ergebnis zum Client.

Wozu ein Clientdatenmodell generieren?

Mit den vom Backend gesendeten JSON-Daten können wir am Client auf drei Arten verfahren:

  1. Verwendung „as is“: In einer Single-Page-App mit einer nicht statisch typisierten Templatesprache könnten wir die Daten direkt an das UI binden. In Angular täten wir das z. B. durch ein *ngFor, mit dem wir über die Kunden iterieren und dann die einzelnen Felder beispielsweise mittels {{customer.lastName}} binden. In großen Applikationen führt das im Lauf der Zeit aber zu immer höherem Wartungsaufwand und Fehlerquellen, da z. B. Tippfehler erst zur Laufzeit aufschlagen. Bei Änderungen des Domänenmodells muss man außerdem die UI-Komponenten manuell durchsuchen und die Data Bindings anpassen.

  2. Manuell das Clientdatenmodell definieren: Für jede Domänenklasse könnten wir für den Client eine TypeScript-Klasse erstellen, die die Serverdaten repräsentiert. Zusammen mit einer statisch typisierten Templatesprache wie React brächte uns das den großen Vorteil einer Überprüfung unserer UI-Komponenten zur Compile-Zeit. Tippfehler oder übersehene Domänenmodelländerungen fallen sofort auf. Der Nachteil dieser Variante liegt offensichtlich im Wartungsaufwand der clientseitigen TypeScript-Klassen.

  3. Am Client das gleiche Domänenmodell wie am Server verwenden: Da wir in unserem Beispiel TypeScript sowohl am Server als auch am Client nutzen, könnten wir das Domänenmodell am Client wiederverwenden. Das wäre auch etwas „objektorientierter“ als nur in Datenfeldern zu denken, bedeutet aber, dass die Objekte vom Server entweder komplett befüllt (alle Properties) geliefert werden oder wir am Client damit umgehen müssen, dass bestimmte Properties nicht befüllt sind. Der erste Fall führt zu Overfetching, der zweite unter Umständen zu Underfetching; nehmen wir in unserem Beispiel an, dass wir eine UI-Komponente designen, die den Vornamen, den Nachnamen und die ID des Kunden enthält. Wenn wir jetzt die Query dahingehend abändern, dass wir die ID daraus entfernen, fällt uns dieses Problem zunächst gar nicht auf. Die App kompiliert einwandfrei und die UI-Komponente scheint auch weiterhin zu funktionieren, allerdings bleibt das Feld ID immer leer.

Doch könnte man diese Nachteile mit unserer MQL-Idee ausgleichen? Die Antwort lautet ganz klar „ja“, denn wir haben alle Informationen an der Hand, die wir zum automatisierten Generieren eines typisierten Clientdatenmodells benötigen: Unser Domänenschema und unsere MQL-Querys, die die retournierten Daten genau beschreiben.

Listing 1: Modul „queries.ts“, Abfrage Customer è Purchases è Tickets

 import { schema } from "../../gen/schema"; import { Query, ROP, MOP, COP } from "../mql/mqlGrammar"; import { Customer, Purchase, SKU, Ticket } from "../../domainModel "; // Define shortcuts for operators. const AND = ROP.and; const OR = ROP.or; const EQ = COP.eq; const GT = COP.gt; const IN = COP.in; const LIKE...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang