Auf einen Blick

  • Das Infrastruktur-Hexagon (Ports & Adapters) beherbergt Outbound Adapter fuer Persistenz (JDBC, JPA/ORM) und Inbound Adapter fuer Netzwerk (REST-/SOAP-Controller).
  • Persistenz kennt zwei grundverschiedene Mechanismen: persistenzorientiert (Bsp. JDBC, jede DB-Interaktion explizit) vs. sammlungsorientiert (Bsp. JPA, Framework entscheidet ueber Zugriff).
  • Datenbankzugriff ist aufwaendig, weil Objekte und Tabellen strukturell verschieden sind und Java/SQL nicht integriert sind - das erzeugt Boilerplate-Code und braucht einen Abbildungsmechanismus.
  • ORM von Domaenenobjekten verletzt ein DDD-Prinzip (Codeabhaengigkeit der Domaene von Technologie); es gibt die Optionen Akzeptieren vs. Kapseln.
  • Netzwerkanbindung: HTTP ist ein zustandsloses Request/Response-Protokoll der Applikationsschicht; Architekturstile darauf sind SOAP und REST.
  • Der Persistenz-Mechanismus tendiert dazu, in die Applikation einzusickern - eine Abwaegung zwischen Akzeptieren und Kapseln.

Kernkonzepte

1. Einordnung: Infrastruktur im Hexagon (logische Sicht)

Die Referenzarchitektur einer Unternehmensanwendung teilt jede Komponente in drei konzentrische Bereiche: Domain (*.domain), Application (*.application) und Infrastructure (*.infra). Die Infrastruktur enthaelt Inbound Adapter und Outbound Adapter; die Applikation enthaelt Inbound Port und Outbound Port; die Domaene enthaelt Wurzelentitaet, Aggregat (mit ID), Entitaet (mit ID), Wertobjekt, Domain Service und Domain Event. Die Codeabhaengigkeit zeigt von aussen nach innen: Infrastruktur haengt von Applikation ab, Applikation von der Domaene. Dieses Kapitel behandelt die konkreten Infrastruktur-Adapter: Persistenz (Outbound) und Netzwerk (Inbound).

2. Persistenz als Outbound Adapter

Persistenz ist ein Outbound Adapter im Infrastrukturhexagon. Die Problemstellung lautet: Adaptieren einer Java-Schnittstelle an eine SQL-Schnittstelle. Aufrufsicht = eine Java-Methode; Datensicht = eine relationale Tabelle (z.B. TABLE EMPLOYEE mit ID, STATUS, FIRST_NAME, LAST_NAME, BIRTH_DAY). Der Outbound Adapter uebersetzt zwischen der objektorientierten Welt (Domaene/Applikation) und der relationalen Welt der Datenbank.

3. JDBC (Java Data Base Connectivity)

JDBC ist die Java-Schnittstelle fuer relationale Datenbanken. Der Schichtenaufbau: JdbcOutboundAdapter (Anwendungscode im Infrastrukturhexagon) nutzt (<<uses>>) die JDBC-API (java.sql.* / javax.sql.*, die fast herstellerunabhaengige Datenbank-API der Java SE); die JDBC-API wird durch einen herstellerabhaengigen JDBC-Driver (com.vendorname.jdbc.*, als JAR-Datei) implementiert (<<implements>>); der Driver nutzt (<<uses>>) die Datenbank. Beispiel im Kurs: JdbcEmployeeOutboundAdapter.

4. JDBC: notwendige Schritte (wer macht was)

Beim reinen JDBC muss der Anwender fast alles selbst tun. Vom Anwender: Datenbankschema erstellen, Verbindung oeffnen, SQL-Statement definieren, Parameter definieren und Werte uebergeben, Statement vorbereiten und ausfuehren, Schleife ueber Ergebnis, Ergebnis in Java-Objekt abbilden, Ausnahmen behandeln, Verbindung und Statement schliessen. Nur Datenquelle initialisieren und Transaktionssteuerung werden (bei Spring) vom Framework uebernommen.

5. JDBC: Nachteile und Vorteile

Aufwaendige und problematische Punkte: zwei Programmiersprachen (Java, SQL) in einer Java-Datei (keine/eingeschraenkte IDE-Unterstuetzung, keine Unterstuetzung durch den Java-Compiler); Datenbankschema muss erzeugt und mit dem Java-Objektmodell konsistent gehalten werden; jeder DB-Hersteller hat seinen SQL-Dialekt (nicht portierbar); Boilerplate-Code (Ressourcen oeffnen/schliessen, Iterieren ueber Ergebnis); herstellerspezifische Ausnahmebehandlung. Vorteile: volle Kontrolle ueber den Programmfluss und ueber die SQL-Verwendung (Optimierungen). Fazit des Kurses: fuer die meisten Szenarien ist die Verwendung der reinen JDBC-API schmerzhaft.

6. JDBC mit Spring

Ein Framework wie Spring reduziert den JDBC-Aufwand deutlich. Spring uebersetzt herstellerspezifische Ausnahmen in eine Java-Ausnahmenhierarchie, nutzt das Entwurfsmuster Template zum Eliminieren von Boilerplate-Code, bietet Dependency Injection der Datenquelle und ist wiederverwendbar fuer alle Abfragen. Ergebnis: Datenbankzugriff reduziert auf das Noetige. Nun uebernimmt Spring: Datenquelle initialisieren, Verbindung oeffnen, Statement vorbereiten und ausfuehren, Schleife ueber Ergebnis, Ausnahmen behandeln, Verbindung/Statement schliessen, Transaktionssteuerung. Beim Anwender verbleiben: Datenbankschema erstellen, SQL-Statement definieren, Parameter/Werte, Ergebnis in Java-Objekt abbilden.

7. ORM mit JPA (Object-Relational-Mapping)

ORM ist die Abbildung von Objektgraphen in eine relationale Datenbank. Beispiel: Java Persistence API (JPA). Schichtaufbau: JdbcOutboundAdapter (Anwendungscode) nutzt JPA (jakarta.persistence.*); ein Persistence Provider (z.B. org.hibernate.orm.*) implementiert JPA; darunter liegen JDBC-API und JDBC-Driver fuer den eigentlichen Datenbankzugriff, dann die Datenbank. JPA sitzt also als Abstraktionsschicht ueber JDBC.

8. JPA-Mapping am Beispiel Stundenzettel

Durchgaengiges Beispiel: Stundenzettel (Aggregat) mit <<RootEntity>> TimeSheet und 1..n <<Entity>> TimeSheetItem. Mapping-Regeln Objekte -> Tabelle: Klasse -> Tabelle; ID -> Primaerschluessel (aus DB-Sequenz); Feld -> Spalte; Referenz -> Relationstabelle oder Fremdschluessel. Zusaetzlich lassen sich Constraints, Nachkommastellen und Feldlaengen ueber Annotationen definieren. Aggregatartiges Verhalten fuer Speichern, Lesen und Loeschen. Das generierte Schema: Tabelle TIME_SHEET (ID PK, EMPLOYEE_ID), Tabelle TIME_SHEET_ITEM (ID PK, HOURS_RECORDED, DATE, COMMENT) und die Relationstabelle TIME_SHEET_ITEMS (TIME_SHEET_ID FK, ITEMS_ID FK).

9. JPA-Datenbankzugriff 1: EntityManager

Der EntityManager ist der Einstiegspunkt in JPA und unterstuetzt alles, was man mit jeder @Entity-Klasse tun kann, die einen Identifizierer hat. Hinzufuegen eines noch nicht gespeicherten Objekts (persist)

  • die TimeSheetItems werden mitgespeichert. Suchen nach Identifizierer (find)
  • die TimeSheetItems werden mitgelesen. Der EntityManager bildet damit die grundlegenden CRUD-artigen Objektoperationen ab.

10. JPA-Datenbankzugriff 2: JPQL

Java Persistence Query Language (JPQL) ist eine SQL-artige Sprache, die auf Objektebene agiert. Man verwendet den Namen der Java-Klasse (nicht der DB-Tabelle) und navigiert entlang des Java-Objektgraphen mit dem Punkt-Operator. JPA/der Persistenz-Provider erzeugt daraus die konkreten SQL-Statements (mit joins ueber die Relationstabellen). JPQL ist die Antwort auf statische, vordefinierte Abfragen.

11. JPA-Datenbankzugriff 3: Criteria API

Die Criteria API dient dem programmatischen Erstellen von Abfragen fuer dynamische Abfragen. Ablauf am Beispiel: 1. Suche nach TimeSheet, 2. Wurzel ist TimeSheet, 3. Selektionskriterien angeben. Anwendungsszenario: Suche eines Geschaeftsobjekts nach dynamischen Suchkriterien, ggf. mit Vergleichsoperatoren (=, >, <, !=, enthaelt, beginnt mit, ...). Sie ist also fuer dynamisch zusammengesetzte Suchen gedacht, wo JPQL zu starr waere.

12. Funktionale Eigenschaften von ORM

Mapping: Konvertierung zwischen Typen der Programmiersprache und Feldtypen der DB; zwischen Java-Klassen und DB-Tabellen/Spalten; Relationen (1:n, n:1, n:m) und Collections; Details des Schemas (Namen, Fremdschluessel, Relationstabellen, Constraints); Abbildung von Vererbungshierarchien (drei Varianten). Operationen: CRUD fuer Objekte; Abfragen ueber JPQL (SQL auf Objektebene) und API zur programmatischen Erzeugung. Kaskadierung: soll ein abhaengiges Objekt bei Relationen mit erzeugt/gespeichert/geloescht werden? Eager vs. Lazy Loading: verbundene Objekte immer laden vs. nur bei Bedarf. Fetch-Graphen: dynamisch auswaehlen, welche Teile eines Objektgraphen geladen werden (gut fuer Performanz).

13. Nichtfunktionale Eigenschaften von ORM

Locking

  • optimistisch: Verwendung einer Versionsspalte, die automatisch erhoeht und geprueft wird; pessimistisch innerhalb einer Transaktion (nicht das klassische pessimistische Sperren): Verhindern von Lesen durch andere Transaktionen und Sperren von Objektgraphen im Kontext optimistischer Sperren. Connection Pooling: Datenbankverbindungen wiederverwenden (Performanz). Caching: Anzahl lesender Zugriffe auf die DB reduzieren (Performanz). Plus laut Kurs 10000 weitere nuetzliche Dinge
  • JPA ist sehr maechtig und komplex.

14. Lazy Loading am Beispiel

Lazy Loading bedeutet: ueber Relationen verbundene Objekte werden nur bei Bedarf geladen. Beispiel: Beim Lesen eines TimeSheet sind die TimeSheetItems zunaechst nur Proxies. Erst beim Zugriff auf den Proxy erfolgt das Nachladen aus der Datenbank. Eager Loading ist das Gegenteil: verbundene Objekte werden immer sofort mitgeladen.

15. Persistenzorientiert vs. Sammlungsorientiert

Persistenz orientiert (Bsp. JDBC): jede Interaktion mit der Datenbank muss explizit vom Anwendercode initiiert werden; volle Kontrolle ueber Datenbankaufrufe. Sammlungsorientiert (Collection Oriented Persistence, Bsp. JPA): der Anwendercode interagiert mit einem Framework, das ueber den Datenbankzugriff entscheidet; Wiederabspeichern und Nachlesen von Objekten erfordert keinen Anwendercode. Bei JPA verhalten sich die vom EntityManager verwalteten Objekte wie Objekte einer Collection. Kernmechanismus: Entitaeten sind unter Kontrolle des EntityManager, solange eine Transaktion offen ist; beim Beenden der Transaktion prueft der EntityManager alle Objekte unter seiner Kontrolle auf Aenderungen und schreibt diese in die DB (dirty checking). Beispiel: save loest kein Schreiben aus, erst bei flush wird geschrieben.

16. Architekturrelevanz der Persistenzwahl

Persistenzorientiert und sammlungsorientiert sind zwei grundsaetzlich verschiedene Persistenz-Mechanismen und nicht auf relationale Datenbanken beschraenkt. Sie haben Einfluss auf den Code im Applikationshexagon, da sich die Steuerung der Persistenz unterscheidet. Die Technologieabhaengigkeit ist damit ins Applikationshexagon subtil eingesickert. Folge: eine Aenderung des Persistenz-Mechanismus ist nicht auf das Infrastrukturhexagon beschraenkt.

17. ORM und Domain Driven Design: das Problem

ORM von Domaenenobjekten verletzt ein Prinzip von Domain Driven Design: es entsteht eine Codeabhaengigkeit der Domaenenobjekte von technologiespezifischem Code (z.B. die @Entity-Annotationen der JPA an den Domaenenklassen). Damit haengt die reine Domaene an einer Infrastrukturtechnologie - genau das will DDD vermeiden.

18. DDD-Loesung Option 1: Akzeptieren

Bei Akzeptieren wird dieselbe Domaenenklasse (z.B. Employee) in allen Schichten mit @Entity annotiert. Vorteile: einfach; sammlungsorientierte Persistenz moeglich. Nachteile: Domaene und Applikation haengen von der Infrastruktur ab; Einschraenkungen am Design des Domaenenmodells - Felder, die Interfaces sind, koennen nicht verwendet werden; komplexe Objektgraphen koennen nicht auf Tabellen abgebildet werden (Tabellen sind ziemlich einfach strukturiert).

19. DDD-Loesung Option 2: Kapseln

Bei Kapseln liegt in der Infrastruktur eine separate @Entity EmployeeTable neben der reinen Domain Entity Employee; die Domaene bleibt frei von JPA. Vorteile: keine Abhaengigkeit von Applikation und Domaene von der Infrastruktur; keine Einschraenkungen am Design des Domaenenmodells. Nachteile: keine sammlungsorientierte Persistenz; kein Lazy-Loading (dynamisches Nachladen); moeglicherweise unnoetige Komplexitaet.

20. Architekturentscheidung ORM/DDD

Leitlinie: generell die einfachste Loesung nehmen, es sei denn, es spricht etwas wirklich dagegen. Fragen: Ist ein Austausch der Persistenz-Technologie wahrscheinlich? (Meist nicht.) Wie stark sind die Einschraenkungen am Design des Domaenenmodells? (Fuer viele, aber nicht alle Anwendungen unkritisch; ORM kann viele, aber nicht alle Objektgraphen behandeln; die Ausnahmen kann man kapseln.) Testbarkeit ist ein gutes Kriterium: keine Probleme mit Unit-Tests der Domaene deuten auf gute Wartbarkeit; JPA-Annotationen stoeren beim Testen nicht. Merksatz: Eine logische Architektur ist kein Selbstzweck - sie hat ihre Begruendung in Wartbarkeit und Betreibbarkeit.

21. Netzwerk: HTTP-Crashkurs

HTTP ist ein Netzwerkprotokoll der Applikationsschicht im OSI/ISO-Modell; Kommunikationsmuster Request/Response; zustandslos (Request/Response-Paare haengen nicht von vorherigen Paaren ab). Request-Kernelemente: URI (z.B. dhbw.de/nghr/XYZ), Methode (POST, GET, PUT, DELETE, ...), Media-Typ (xml, json, gif, application/excel), optionale Parameter (?sort=true), optionale Payload. Response-Kernelemente: Status (1xx, 2xx, 3xx, 4xx, 5xx), Media-Typ, optionale Payload.

22. Netzwerk: Problemstellung Webanbindung

Ein Client moechte Methoden auf dem Server ueber das Netzwerk aufrufen (z.B. Employee hire(EmployeeHireDTO), Employee get(Long id), void changeAddress(AddressDTO), void retire(Long id) im Infrastrukturhexagon des Servers). Offene Fragen: Wie verwenden wir ein Netzwerkprotokoll wie HTTP dafuer (Nutzungskonzept)? Wie binden wir HTTP-Request und HTTP-Response an die Methoden? Die HTTP-Spezifikation sagt uns nicht, wie das geht - dafuer braucht es Architekturstile (SOAP, REST).

23. SOAP (Simple Object Access Protocol)

Bei SOAP legt die Web Service Definition Language (WSDL), eine XML-Datei, die Signaturen der verfuegbaren Methoden sowie die Datentypen fuer Parameter und Rueckgabewerte fest. Serverseitig gibt es eine URL pro WSDL, an die alle Requests geschickt werden. Die Payload enthaelt als XML alle weiteren Informationen: welche Methode aufgerufen wird und ihre Parameter. Vorteile: Interoperabilitaet zwischen Programmiersprachen (XML als neutrales Format); Requests koennen gegen WSDL validiert werden; protokollunabhaengig (HTTP, SMTP, FTP, ...). Nachteile: erfordert Erstellung/Verwendung der WSDL-Datei; Ausnutzung von Protokollfeatures nicht moeglich.

24. REST: Grundidee und Ressourcen

REST (REpresentational State Transfer) = Transfer des Zustandes durch seine Darstellung. Im Server gibt es Entitaeten (die Domaenenobjekte); diese werden auf Ressourcen abgebildet, wenn Clients an ihnen interessiert sind (exponierte Daten der Entitaet, DTOs, Listen). Es gibt auch Ressourcen ohne zugeordnete Entitaet, z.B. Commands (Auftraege). Eine Ressource wird durch eine URI identifiziert; URL = Protokoll + URI (Bsp. https://nghr.dhbw.de/hrpayroll/api/v1/employees/004825). Eine Ressource kann mehrere Repraesentationen haben (Mitarbeiter als XML, JSON, GIF oder PDF) und besteht aus: Daten der Ressource, Metadaten und Hypermedia Links (zur Navigation). Client-Operationen: Anlegen, Lesen, Aendern, Loeschen.

25. REST: Hypermedia Links und Zustandslosigkeit

Hypermedia Links erlauben dem Client, die Ressource zu aendern oder auf eine verknuepfte Ressource zuzugreifen (Navigation), z.B. vom Mitarbeiter zur Gehaltsabrechnung - die URI dafuer wird in der Response mitgegeben. Zustandslos in drei Hinsichten: kein Protokollzustand (HTTP erfuellt dies); Anwendungszustand wird nur auf dem Client gespeichert und muss dem Server bei jedem Aufruf mitgeteilt werden (z.B. Session Cookie); Ressourcenzustand wird auf dem Server / der Datenbank gespeichert und ist allen Clients zugaenglich. Der Anwendungszustand wird zum Client in Form der Repraesentation der Ressourcen transferiert.

26. REST: Vorteile und Nachteile

Vorteile: Zustandslosigkeit erlaubt Skalierbarkeit und Performanz (Lastverteilung pro Request, nicht pro Session/Client; Caching, wenn sich eine Ressource nicht geaendert hat); Ausnutzung von Protokollfeatures (bei HTTP: URI, Methode, Media-Typ, Return Code); einfaches und schnell verstaendliches Programmiermodell (jede REST-API funktioniert aehnlich, kein WSDL noetig; Hypermedia Links fuehren den generischen Client). Nachteile: das Format der Payload ist nicht spezifiziert (echte generische Clients werden nicht unterstuetzt); keine Unterstuetzung zur Validierung der Payload.

27. REST und HTTP: Verhaeltnis

REST ist ein Architekturstil fuer Netzwerkkommunikation und stellt grundlegende Prinzipien auf; im Kurs wurde es am Beispiel REST mit HTTP kennengelernt. Die REST-Prinzipien sind protokollunabhaengig - in der Originalarbeit zu REST wird HTTP nicht erwaehnt. HTTP ist fuer REST jedoch ein geeignetes Protokoll und unterstuetzt die REST-Prinzipien; daher sind REST-APIs faktisch immer HTTP-APIs.

28. REST-Controller als Inbound Adapter

Der REST-Controller ist der Inbound Adapter. Beispiel Mitarbeiter einstellen (Ressource anlegen): ein gemeinsamer Teil der URI gilt fuer alle Methoden der Klasse; es besteht eine Abhaengigkeit zum Applikationshexagon (die Schnittstelle wird vom InboundPort implementiert); die Methode ist an HTTP POST mit JSON-Repraesentation gebunden; das Framework wandelt JSON in ein Java-Objekt um; der InboundPort im Applikationshexagon wird aufgerufen; die URI der neu erzeugten Ressource wird berechnet; Response-Status 201 Created mit der URL der neuen Ressource im HTTP-Header location. Anzeigen: GET auf api/v1/employees (alle) bzw. api/v1/employees/{id} (einer), Antwort 200 OK mit JSON bzw. 404 NOT FOUND. Bild anzeigen: genau dieselbe URI, aber andere Repraesentation derselben Ressource, 200 OK mit Bild-Repraesentation.

29. Zentrale Fehlerbehandlung im REST-Controller

Beispiel Behandlung aller Validierungsfehler: eine Klasse wird als Klasse mit Call-Backs fuer REST-Controller registriert; eine Methode wird als Ausnahmebehandlung fuer einen bestimmten Ausnahmetyp registriert; eine selbstgebaute Klasse nimmt die Details der Fehlermeldungen auf; die relevanten Informationen ueber den Validierungsfehler werden extrahiert; es wird ein HTTP 400 BAD REQUEST Response mit den Fehlerdetails erstellt.

30. Regeln fuer gute REST-Controller

REST-Prinzipien folgen: HTTP-Methoden gemaess Spezifikation (POST, GET, PUT, DELETE), HTTP-Statuscodes gemaess Spezifikation (1xx-5xx), Nomen im Plural fuer URI-Namen (employees, nicht employee oder getEmployee), Ressourcen durch URI identifiziert (Identifizierer im URI: /employees/28374/), optional Hypermedia-Links. API versionieren (nghr.de/api/v12/) fuer sukzessive Client-Umstellung. Konsistente URL-Parameternamen fuer Sortieren, Paginieren, Filtern, Projizieren (?sort=firstname). Zentrale Fehlerbehandlung: alle Ausnahmen in HTTP-Statuscodes uebersetzen, Fehlerdetails in die Payload, einheitliches Format. Sicherheit: Authentifizierung, HTTPS, Autorisierung auf URI-Namen. Dokumentation mit Tools (z.B. Swagger). Caching ueber Cache-Control- und ETag-Header. Logging aller Requests. Testen mit Unit-Tests/Mocks und Werkzeugen wie Postman/Newman oder Bruno.

31. REST-Praxis: URI-Design und Statuscodes

URI-Namensgebung: Nomen im Plural (payrolls/{id}/). Verschachteln der Ressourcen bei hierarchischen Objekten (../employees/{id}/payrolls/{month_year}). HTTP-Methoden in HTTP-Semantik verwenden (wichtigste: POST, PUT, GET, DELETE). URL-Parameter fuer Sortierung, Filterung, Paginierung. Ausnahmen/Validierungsfehler in HTTP-Statuscodes konvertieren: 400er wenn der Client schuld ist, 500er wenn der Server Probleme macht. API-Versionierung (api/v17/), sonst muessen eines Tages alle Clients am selben Tag umgestellt werden. API-Dokumentation optional mit Swagger. Sicherheit: Berechtigungspruefung auf HTTP-Methode und URI-Muster.

Merksätze

Persistenz ist ein Outbound Adapter
Persistenz sitzt als Outbound Adapter im Infrastrukturhexagon; ihre Aufgabe ist das Adaptieren einer Java-Schnittstelle an eine SQL-Schnittstelle (Aufrufsicht vs. Datensicht).
Persistenzorientiert vs. Sammlungsorientiert
JDBC ist persistenzorientiert (jede DB-Interaktion explizit, volle Kontrolle), JPA ist sammlungsorientiert (das Framework entscheidet ueber den Datenbankzugriff, verwaltete Objekte verhalten sich wie eine Collection).
EntityManager-Dirty-Checking
Solange eine Transaktion offen ist, stehen Entitaeten unter Kontrolle des EntityManager; beim Beenden der Transaktion prueft er alle verwalteten Objekte auf Aenderungen und persistiert sie. save schreibt nicht sofort, erst flush schreibt in die Datenbank.
ORM verletzt ein DDD-Prinzip
ORM von Domaenenobjekten erzeugt eine Codeabhaengigkeit der Domaene von technologiespezifischem Code (z.B. @Entity) und verletzt damit ein DDD-Prinzip; man kann es Akzeptieren oder Kapseln.
Akzeptieren vs. Kapseln
Akzeptieren ist einfach und erlaubt sammlungsorientierte Persistenz, koppelt aber Domaene/Applikation an die Infrastruktur; Kapseln haelt die Domaene frei, verliert aber sammlungsorientierte Persistenz und Lazy-Loading.
HTTP ist zustandslos
HTTP ist ein Request/Response-Protokoll der Applikationsschicht (OSI/ISO) und zustandslos: Request/Response-Paare haengen nicht von vorherigen Paaren ab. Kernelemente: Methode, URI mit Parametern, Media-Typ, Status-Code, Payload.
SOAP braucht eine WSDL
SOAP definiert Methodensignaturen und Datentypen in einer WSDL-Datei (ein Endpunkt pro WSDL), transportiert alles als XML-Payload, ist protokollunabhaengig - kann aber Protokollfeatures nicht ausnutzen.
REST transferiert Zustand als Repraesentation
REST bildet Entitaeten auf Ressourcen ab, die durch eine URI identifiziert werden und mehrere Repraesentationen (Media-Typen) haben koennen; Zustandslosigkeit ist bei REST vorgeschrieben (im Gegensatz zu SOAP).
REST-Prinzipien sind protokollunabhaengig
REST ist ein Architekturstil und nicht von HTTP abhaengig; de facto werden aber alle REST-Services mit HTTP implementiert, weil HTTP die REST-Prinzipien gut unterstuetzt.
Logische Architektur ist kein Selbstzweck
Eine logische Architektur hat ihre Begruendung in Wartbarkeit und Betreibbarkeit; im Zweifel die einfachste Loesung nehmen, es sei denn, es spricht etwas wirklich dagegen.

Klausur-Stichworte (aus der Zusammenfassung)

Theißen hat gesagt: alle prüfbaren Themen tauchen als Stichwort in den Zusammenfassungs-Folien auf. Genau diese Stichworte stehen hier.

📌 Zusammenfassung dieses Kapitels
  • Datenbankzugriff ist aufwaendig: Objekte und Tabellen sind strukturell verschieden
  • Tabellen sind in der Programmiersprache meist nicht integriert - man braucht einen Abbildungsmechanismus
  • Datenbanksprache und Programmiersprache sind meist nicht integriert - fuehrt zu viel Boilerplate-Code
  • JDBC als Beispiel fuer persistenzorientierten Datenbankzugriff: volle Kontrolle ueber die Datenbank
  • JDBC: volle Ausnutzung von Datenbankfeatures moeglich
  • JDBC: Zugriff fuer ein Aggregat muss manuell implementiert werden (durch Objektgraph navigieren)
  • JDBC: Framework/Bibliotheken erleichtern die Entwicklung erheblich - unbedingt verwenden
  • JPA als Beispiel fuer sammlungsorientierten Datenbankzugriff: nur ORM-Mapping erforderlich
  • JPA: teilweise Integration der Datenbanksprache in die Programmiersprache
  • JPA: Persistenz-Provider entscheidet ueber den Datenbankzugriff - Spezifikation gut verstehen
  • JPA: nur teilweise Kontrolle ueber die Datenbank, manche Features nicht nutzbar
  • Einfluss auf die logische Architektursicht: der Persistenz-Mechanismus sickert in die Anwendung ein (Abhaengigkeiten und Design)
  • Abwaegung: inwieweit akzeptieren oder besser kapseln
  • HTTP-Protokoll: Applikationsschicht im OSI/ISO-Modell, Request/Reply, zustandslos
  • HTTP-Kernelemente: Methode, URI mit URI-Parametern, Media-Typ, Status-Code, Payload
  • SOAP: Methodensignaturen und Datenstrukturen in WSDL-Datei spezifiziert, ein Endpunkt pro WSDL
  • SOAP: Protokoll kann HTTP sein, andere moeglich (SMTP, FTP); Payload in XML; Zustandslosigkeit moeglich aber nicht vorgeschrieben
  • REST: Architekturstil, nicht von HTTP abhaengig, de facto immer mit HTTP implementiert
  • REST: serverseitige Entitaeten werden als Ressource dargestellt, durch URI identifiziert
  • REST: eine Ressource kann verschiedene Repraesentationen haben (Media-Typ); Zustandslosigkeit ist vorgeschrieben
  • API-Design: URL-Aufbau, API-Versionierung und Dokumentation, Fehlerbehandlung, Sicherheit

Übungsfragen

F1 In welchem Teil des Hexagons ist die Persistenz angesiedelt und welche Aufgabe hat sie dort?

Die Persistenz ist ein Outbound Adapter im Infrastrukturhexagon. Ihre Aufgabe ist das Adaptieren einer Java-Schnittstelle an eine SQL-Schnittstelle, also die Vermittlung zwischen der Aufrufsicht (Java-Methode) und der Datensicht (relationale Tabelle). Damit uebersetzt sie zwischen der objektorientierten Welt der Domaene/Applikation und der relationalen Datenbank.

F2 Warum ist Datenbankzugriff laut der Zusammenfassung grundsaetzlich aufwaendig?

Weil Objekte und Tabellen strukturell verschieden sind und Tabellen in der Programmiersprache meist nicht integriert sind, sodass man einen Abbildungsmechanismus braucht. Zusaetzlich sind Datenbanksprache (SQL) und Programmiersprache (Java) meist nicht integriert. Beides fuehrt zu viel Boilerplate-Code.

F3 Erklaeren Sie den Schichtenaufbau von JDBC (Adapter, API, Driver) und welche Teile herstellerabhaengig sind.

Der JdbcOutboundAdapter (Anwendungscode im Infrastrukturhexagon) nutzt die JDBC-API (java.sql.* / javax.sql.*), die fast herstellerunabhaengige Datenbank-API der Java SE. Diese API wird durch einen JDBC-Driver (com.vendorname.jdbc.*) implementiert, der herstellerabhaengig ist und als JAR-Datei vorliegt. Der Driver greift schliesslich auf die Datenbank zu. Herstellerabhaengig ist also der Driver, nicht die API.

F4 Nennen Sie die wesentlichen Nachteile der reinen JDBC-API.

Es vermischen sich zwei Programmiersprachen (Java und SQL) in einer Java-Datei, wodurch IDE- und Compiler-Unterstuetzung fehlen. Das Datenbankschema muss erzeugt und mit dem Java-Objektmodell konsistent gehalten werden. Der SQL-Dialekt ist herstellerspezifisch und damit nicht portierbar. Es entsteht viel Boilerplate-Code (Ressourcen oeffnen/schliessen, Iterieren) und die Ausnahmebehandlung ist herstellerspezifisch.

F5 Wie reduziert Spring den JDBC-Aufwand?

Spring uebersetzt herstellerspezifische Ausnahmen in eine einheitliche Java-Ausnahmenhierarchie, verwendet das Entwurfsmuster Template zum Eliminieren von Boilerplate-Code und bietet Dependency Injection der Datenquelle. Der Datenbankzugriff wird dadurch auf das Noetige reduziert und ist fuer alle Abfragen wiederverwendbar. Spring uebernimmt u.a. Verbindung oeffnen/schliessen, Statement ausfuehren, Ergebnisschleife, Ausnahmebehandlung und Transaktionssteuerung.

F6 Was versteht man unter Object-Relational-Mapping (ORM) und wie ordnet sich JPA in die Technologiestapel ein?

ORM ist die Abbildung von Objektgraphen in eine relationale Datenbank. JPA (Java Persistence API, jakarta.persistence.*) ist ein Beispiel dafuer. Der Anwendungscode nutzt JPA; ein Persistence Provider (z.B. Hibernate) implementiert JPA; darunter liegen die JDBC-API und der JDBC-Driver fuer den eigentlichen Datenbankzugriff. JPA sitzt also als Abstraktionsschicht ueber JDBC.

F7 Nennen Sie die drei Arten des Datenbankzugriffs, die JPA anbietet, und wofuer sie jeweils gedacht sind.

Erstens der EntityManager als Einstiegspunkt fuer objektbasierte CRUD-Operationen (persist, find etc.). Zweitens JPQL, eine SQL-artige Sprache auf Objektebene mit Klassennamen und Navigation ueber den Punkt-Operator, gedacht fuer vordefinierte Abfragen. Drittens die Criteria API zum programmatischen Erstellen dynamischer Abfragen, wenn Suchkriterien und Vergleichsoperatoren erst zur Laufzeit feststehen.

F8 Erklaeren Sie den Unterschied zwischen Eager Loading und Lazy Loading am Beispiel des Stundenzettels.

Bei Eager Loading werden ueber Relationen verbundene Objekte immer sofort mitgeladen, bei Lazy Loading nur bei Bedarf. Beispiel: Liest man ein TimeSheet, sind die zugehoerigen TimeSheetItems zunaechst nur Proxies. Erst beim Zugriff auf einen Proxy werden die Items aus der Datenbank nachgeladen. Lazy Loading spart so unnoetige Datenbankzugriffe.

F9 Was ist der Unterschied zwischen persistenzorientierter und sammlungsorientierter Persistenz?

Bei persistenzorientierter Persistenz (Beispiel JDBC) muss jede Interaktion mit der Datenbank explizit vom Anwendercode initiiert werden, man hat volle Kontrolle ueber die Datenbankaufrufe. Bei sammlungsorientierter Persistenz (Beispiel JPA) interagiert der Anwendercode mit einem Framework, das ueber den Datenbankzugriff entscheidet; Wiederabspeichern und Nachlesen von Objekten erfordert keinen expliziten Anwendercode, die verwalteten Objekte verhalten sich wie eine Collection.

F10 Wie sorgt der EntityManager bei JPA dafuer, dass Aenderungen persistiert werden?

Solange eine Transaktion offen ist, stehen die Entitaeten unter Kontrolle des EntityManager. Aenderungen am Objekt geschehen zunaechst nur im Speicher. Beim Beenden der Transaktion prueft der EntityManager alle Objekte unter seiner Kontrolle auf Aenderungen und persistiert diese (Dirty Checking). save loest also kein sofortiges Schreiben aus, erst bei flush bzw. Transaktionsende wird in die Datenbank geschrieben.

F11 Warum ist die Wahl des Persistenz-Mechanismus architekturrelevant und nicht nur ein Infrastruktur-Detail?

Weil die Steuerung der Persistenz sich zwischen den Mechanismen unterscheidet und dadurch Einfluss auf den Code im Applikationshexagon hat. Die Technologieabhaengigkeit sickert subtil ins Applikationshexagon ein. Folge: eine Aenderung des Persistenz-Mechanismus ist nicht auf das Infrastrukturhexagon beschraenkt, sondern zieht Anpassungen in der Applikation nach sich.

F12 Welches DDD-Prinzip verletzt das ORM von Domaenenobjekten und warum?

Es verletzt das Prinzip, dass die Domaene frei von technologiespezifischem Code sein soll. Durch die JPA-Annotationen wie @Entity an den Domaenenklassen entsteht eine Codeabhaengigkeit der Domaenenobjekte von der Persistenztechnologie. Die reine Domaene haengt damit an der Infrastruktur, was DDD gerade vermeiden will.

F13 Vergleichen Sie die Optionen Akzeptieren und Kapseln bei ORM und DDD.

Akzeptieren annotiert die Domaenenklasse selbst mit @Entity: einfach und mit sammlungsorientierter Persistenz moeglich, aber Domaene und Applikation haengen von der Infrastruktur ab und das Domaenenmodell wird eingeschraenkt (keine Interface-Felder, keine komplexen Objektgraphen). Kapseln legt eine separate @Entity-Tabellenklasse in der Infrastruktur an: die Domaene bleibt frei und uneingeschraenkt, aber es gibt keine sammlungsorientierte Persistenz und kein Lazy-Loading, und es entsteht moeglicherweise unnoetige Komplexitaet.

F14 Welche Kriterien nennt der Kurs fuer die Architekturentscheidung zwischen Akzeptieren und Kapseln?

Grundsatz ist, die einfachste Loesung zu nehmen, sofern nichts wirklich dagegen spricht. Man fragt, ob ein Austausch der Persistenz-Technologie wahrscheinlich ist (meist nicht) und wie stark die Einschraenkungen am Domaenenmodell sind (fuer viele Anwendungen unkritisch, Ausnahmen kann man kapseln). Ein gutes Kriterium ist die Testbarkeit: wenn Unit-Tests der Domaene problemlos moeglich sind, deutet das auf gute Wartbarkeit, und JPA-Annotationen stoeren beim Testen nicht.

F15 Nennen Sie die Eigenschaften von HTTP und seine Kernelemente.

HTTP ist ein Netzwerkprotokoll der Applikationsschicht im OSI/ISO-Modell, folgt dem Request/Response-Kommunikationsmuster und ist zustandslos, das heisst Request/Response-Paare haengen nicht von vorherigen Paaren ab. Kernelemente sind die Methode (POST, GET, PUT, DELETE), die URI mit URI-Parametern, der Media-Typ (z.B. xml, json, gif), der Status-Code (1xx bis 5xx) und die optionale Payload.

F16 Beschreiben Sie SOAP und die Rolle der WSDL.

SOAP (Simple Object Access Protocol) verwendet eine WSDL-Datei, eine XML-Datei, die die Signaturen der verfuegbaren Methoden sowie die Datentypen fuer Parameter und Rueckgabewerte festlegt. Es gibt serverseitig eine URL pro WSDL, an die alle Requests geschickt werden. Die XML-Payload enthaelt alle weiteren Informationen wie die aufzurufende Methode und deren Parameter. SOAP ist interoperabel und protokollunabhaengig, kann aber Protokollfeatures nicht ausnutzen und erfordert die WSDL.

F17 Was bedeutet REST und wie haengen Entitaet, Ressource, URI und Repraesentation zusammen?

REST steht fuer REpresentational State Transfer, also Transfer des Zustandes durch seine Darstellung. Serverseitige Entitaeten (Domaenenobjekte) werden auf Ressourcen abgebildet, wenn Clients daran interessiert sind. Jede Ressource wird durch eine URI identifiziert und kann mehrere Repraesentationen haben (z.B. Mitarbeiter als XML, JSON, GIF oder PDF). Eine Repraesentation besteht aus Daten der Ressource, Metadaten und Hypermedia Links.

F18 Was bedeutet Zustandslosigkeit bei REST und welche drei Zustandsarten werden unterschieden?

Zustandslosigkeit bedeutet, dass es keinen Protokollzustand gibt (HTTP erfuellt dies). Unterschieden werden Anwendungszustand, der nur auf dem Client gespeichert ist und dem Server bei jedem Aufruf mitgeteilt werden muss (z.B. per Session Cookie), und Ressourcenzustand, der auf dem Server bzw. in der Datenbank gespeichert und allen Clients zugaenglich ist. Der Anwendungszustand wird zum Client in Form der Repraesentation der Ressourcen transferiert.

F19 Nennen Sie Vorteile und Nachteile von REST.

Vorteile: Die Zustandslosigkeit erlaubt Skalierbarkeit und Performanz durch Lastverteilung pro Request und Caching; Protokollfeatures wie URI, Methode, Media-Typ und Return Code werden ausgenutzt; das Programmiermodell ist einfach und schnell verstaendlich, weil jede REST-API aehnlich funktioniert und Hypermedia Links den Client fuehren. Nachteile: Das Format der Payload ist nicht spezifiziert, sodass wirklich generische Clients nicht unterstuetzt werden, und es gibt keine Unterstuetzung zur Validierung der Payload.

F20 In welchem Verhaeltnis stehen REST und HTTP zueinander?

REST ist ein Architekturstil mit protokollunabhaengigen Prinzipien; in der Originalarbeit zu REST wird HTTP nicht einmal erwaehnt. HTTP ist jedoch ein fuer REST geeignetes Protokoll, weil es die REST-Prinzipien unterstuetzt. Daher werden REST-APIs faktisch immer als HTTP-APIs implementiert.

F21 Welche Regeln nennt der Kurs fuer gute REST-Controller hinsichtlich URI-Namen und HTTP-Verwendung?

URI-Namen sollen Nomen im Plural sein (employees, nicht employee oder getEmployee), Ressourcen werden durch Identifizierer im URI adressiert (/employees/28374/), hierarchische Objekte werden verschachtelt (/employees/{id}/payrolls/{month_year}). HTTP-Methoden und Statuscodes sind gemaess Spezifikation zu verwenden. URL-Parameter dienen dem Sortieren, Filtern, Paginieren und Projizieren mit konsistenten Namen (?sort=firstname).

F22 Wie werden Fehler in einem REST-Controller behandelt und welche Statuscodes gelten wann?

Es gibt eine zentrale Fehlerbehandlung: eine Klasse wird mit Call-Backs fuer REST-Controller registriert, eine Methode als Ausnahmebehandlung fuer einen Ausnahmetyp; die Fehlerdetails werden in einer eigenen Klasse gesammelt und als Payload zurueckgegeben. Alle Ausnahmen und Validierungsfehler werden in HTTP-Statuscodes uebersetzt: 400er-Codes, wenn der Client schuld ist (z.B. 400 Bad Request bei Validierungsfehlern), und 500er-Codes, wenn der Server Probleme hat. Wichtig ist ein strukturiertes, einheitliches Format.

F23 Warum ist API-Versionierung bei REST wichtig?

Die Versionierung (z.B. api/v12/) erlaubt es, Clients sukzessive auf eine neue Version umzustellen. Ohne Versionierung muesste man eines Tages alle Clients am selben Tag umstellen, was praktisch nicht handhabbar ist.

F24 Nennen Sie am Beispiel Mitarbeiter einstellen, wie ein REST-Controller den Ablauf und die HTTP-Antwort gestaltet.

Die Methode ist an HTTP POST mit einer JSON-Repraesentation der Ressource gebunden; ein gemeinsamer URI-Teil gilt fuer alle Methoden der Klasse. Das Framework wandelt das JSON in ein Java-Objekt um, dann wird der InboundPort im Applikationshexagon aufgerufen. Anschliessend wird die URI der neu erzeugten Ressource berechnet und ein Response mit Status 201 Created zurueckgegeben, wobei die URL der neuen Ressource im HTTP-Header location steht.