Architektur-Überblick
Zielgruppe: Architekt:in. Dieses Kapitel beantwortet "was gibt es, warum sieht es so aus, wie hängt es zusammen" - nicht "wie schreibe ich Code dafür" (das ist 02-entwicklung).
C4-Modell: Kontext (Ebene 1)
Wer benutzt das System, womit spricht es nach außen?
C4Context
title Bibliotheks-Plattform - Systemkontext
Person(member, "Bibliotheksmitglied", "sucht Bücher, leiht aus, sieht Mahngebühren")
Person(librarian, "Bibliothekspersonal", "verwaltet Ausleihen, Bestand, Mitglieder")
System(library, "Library Platform", "Enterprise-Bibliotheksverwaltung (dieses Repo)")
System_Ext(keycloak, "Keycloak", "Identity Provider (OAuth2/OIDC)")
System_Ext(smtp, "E-Mail (simuliert)", "Benachrichtigungsversand (notification-service loggt statt sendet)")
Rel(member, library, "nutzt", "HTTPS/Angular-SPA")
Rel(librarian, library, "nutzt", "HTTPS/Angular-SPA")
Rel(library, keycloak, "authentifiziert via", "OIDC")
Rel(library, smtp, "benachrichtigt via", "simuliert")
C4-Modell: Container (Ebene 2)
C4Container
title Bibliotheks-Plattform - Container
Person(user, "Nutzer:in", "Mitglied oder Personal")
Container(spa, "Angular-Frontend", "Angular 22", "SPA, PKCE-Login gegen Keycloak")
Container(gateway, "api-gateway", "Spring Cloud Gateway (WebFlux)", "Routing, JWT-Prüfung, Single Entry Point")
Container(catalog, "catalog-service", "Spring Boot", "Bücher, Exemplare, Suche")
Container(member, "member-service", "Spring Boot", "Mitglieder, Tarife")
Container(lending, "lending-service", "Spring Boot", "Ausleihe/Rückgabe (Saga, Outbox)")
Container(reservation, "reservation-service", "Spring Boot", "Vormerkungen")
Container(fine, "fine-service", "Spring Boot", "Mahngebühren")
Container(notification, "notification-service", "Spring Boot", "Benachrichtigungen")
Container(config, "config-server", "Spring Cloud Config", "zentrale Konfiguration")
Container(discovery, "discovery-server", "Netflix Eureka", "Service Registry (Compose-Profil)")
ContainerDb(pg, "PostgreSQL", "eine Instanz, 6 DBs", "je Service eine logische Datenbank")
ContainerQueue(kafka, "Kafka", "Event-Bus", "Domain Events")
ContainerQueue(rabbit, "RabbitMQ", "Task-Queue", "Commands")
Container(redis, "Redis", "Cache", "Read-Through-Cache für Katalogsuche")
Container(keycloak, "Keycloak", "OIDC-Provider", "Auth")
Rel(user, spa, "benutzt")
Rel(spa, gateway, "REST/JSON, JWT im Header")
Rel(gateway, catalog, "routet")
Rel(gateway, member, "routet")
Rel(gateway, lending, "routet")
Rel(gateway, reservation, "routet")
Rel(gateway, fine, "routet")
Rel(lending, catalog, "prüft Verfügbarkeit", "REST (OpenFeign, Circuit Breaker)")
Rel(lending, member, "prüft Berechtigung", "REST (OpenFeign, Circuit Breaker)")
Rel(lending, kafka, "veröffentlicht LoanCreated/-Returned/-Overdue", "Outbox")
Rel(reservation, kafka, "konsumiert CopyAvailable, veröffentlicht ReservationCreated")
Rel(fine, kafka, "konsumiert LoanOverdue, veröffentlicht FineIssued")
Rel(notification, kafka, "konsumiert alle Events")
Rel(notification, rabbit, "konsumiert SendNotificationCommand")
Rel(catalog, pg, "liest/schreibt")
Rel(member, pg, "liest/schreibt")
Rel(lending, pg, "liest/schreibt")
Rel(catalog, redis, "Cache-Aside")
Rel(spa, keycloak, "Login (Authorization Code + PKCE)")
Rel(gateway, keycloak, "validiert JWT")
Bounded Contexts (fachliche Landkarte)
| Bounded Context | Service | Kernaggregat |
|---|---|---|
| Katalog | catalog-service |
Book (mit Copy-Exemplaren) |
| Mitgliedschaft | member-service |
Member |
| Ausleihe | lending-service |
Loan |
| Vormerkung | reservation-service |
Reservation |
| Gebühren | fine-service |
Fine |
| Benachrichtigung | notification-service |
(kein eigenes Aggregat - reiner Übersetzer/Ausführer) |
Details zu den fachlichen Regeln je Bounded Context: siehe domain-model.md. Details zu den ADRs, die diese Struktur begründen: siehe adr/.
Warum diese Struktur?
Die Bounded-Context-Grenzen folgen fachlichen Zuständigkeiten (nicht z. B. rein technischen
Schichten) - ein zentrales DDD-Prinzip. Ein Hinweis, ob ein Konzept in den richtigen Context
gehört: "Wer ändert dieses Datum typischerweise, und warum?" Ein Exemplarstatus
(VERFÜGBAR/AUSGELIEHEN) wird z. B. sowohl von catalog-service (Bestandsführung) als auch
indirekt von lending-service (durch eine Ausleihe) beeinflusst - deshalb kommuniziert
lending-service diese Änderung per Event (LoanCreated/LoanReturned), statt selbst in die
Datenbank von catalog-service zu schreiben (siehe ADR-0002).