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).

⌂ Cockpit