feat(build): Refactor infrastructure modules and establish single source of truth

This commit introduces a major refactoring of the build system and the core infrastructure modules. The primary goal is to establish a strict "Single Source of Truth" for all dependencies using Gradle Version Catalogs and to create a clean, maintainable, and scalable foundation for all current and future services.

### 1. Centralized Dependency Management (`libs.versions.toml`)

- **Established Single Source of Truth:** All dependency versions are now exclusively managed in `gradle/libs.versions.toml`. Hardcoded versions have been removed from all build scripts.
- **Introduced Gradle Bundles:** To simplify module dependencies, several bundles have been created (e.g., `testing-jvm`, `redis-cache`, `spring-cloud-gateway`, `monitoring-client`). This drastically reduces boilerplate in the `build.gradle.kts` files and improves readability.
- **Cleaned up Aliases:** All library and plugin aliases have been standardized for consistency.

### 2. Infrastructure Module Refactoring

All infrastructure modules (`core`, `platform`, `auth`, `cache`, `event-store`, `messaging`, `monitoring`, `gateway`) have been refactored to align with the new dependency management strategy.

- **Simplified Build Scripts:** The `build.gradle.kts` for each module now uses the new bundles and aliases, making them significantly cleaner and easier to understand.
- **Consistent Structure:** The architecture of each module now clearly follows the Port-Adapter pattern where applicable (e.g., `cache-api`/`redis-cache`).
- **Standardized `platform-bom`:** The project's own Bill of Materials (`platform-bom`) now also includes the Spring Cloud BOM, ensuring version consistency for all Spring-related dependencies.

### 3. Added Infrastructure Documentation

To improve onboarding and architectural understanding, a dedicated `README-*.md` file has been created for each refactored infrastructure module:
- `README-CORE.md`
- `README-PLATFORM.md`
- `README-INFRA-AUTH.md`
- `README-INFRA-CACHE.md`
- `README-INFRA-EVENT-STORE.md`
- `README-INFRA-MESSAGING.md`
- `README-INFRA-MONITORING.md`
- `README-INFRA-GATEWAY.md`

These documents explain the purpose, architecture, and usage of each component within the system. This lays the groundwork for our "Tracer Bullet" development approach.
This commit is contained in:
stefan
2025-07-31 14:09:22 +02:00
parent 81cb4582d6
commit df5919fac8
27 changed files with 882 additions and 356 deletions
+51
View File
@@ -0,0 +1,51 @@
# Infrastructure/Auth Module
## Überblick
Das **Auth-Modul** ist die zentrale Komponente für die gesamte Authentifizierung und Autorisierung innerhalb der Meldestelle-Systemlandschaft. Es ist verantwortlich für die Absicherung von APIs, die Validierung von Benutzeridentitäten und die Verwaltung von Berechtigungen.
Als Identity Provider wird **Keycloak** verwendet. Dieses Modul kapselt die gesamte Interaktion mit Keycloak und stellt dem Rest des Systems eine einheitliche und vereinfachte Sicherheitsschicht zur Verfügung.
## Architektur
Das Auth-Modul ist in zwei spezialisierte Komponenten aufgeteilt, um eine klare Trennung der Verantwortlichkeiten zu gewährleisten:
infrastructure/auth/
├── auth-client/ # Wiederverwendbare Bibliothek für die JWT-Validierung
└── auth-server/ # Eigenständiger Service für Benutzerverwaltung & Token-Austausch
### `auth-client`
Dieses Modul ist eine **wiederverwendbare Bibliothek** und kein eigenständiger Service. Es enthält die gesamte Logik, die andere Microservices (wie `masterdata-service`, `members-service` etc.) benötigen, um ihre Endpunkte abzusichern.
**Hauptaufgaben:**
* **JWT-Validierung:** Stellt Spring Security Konfigurationen bereit, um eingehende JWTs (ausgestellt von Keycloak) zu validieren. Es prüft die Signatur, den Aussteller (Issuer) und die Gültigkeitsdauer des Tokens.
* **Rechte-Extraktion:** Extrahiert die Rollen und Berechtigungen des Benutzers aus dem validierten Token.
* **Security Context:** Befüllt den `SecurityContextHolder` von Spring, sodass in den Controllern einfach auf den authentifizierten Benutzer zugegriffen werden kann (z.B. mit `@AuthenticationPrincipal`).
Jeder Microservice, der geschützte Endpunkte anbietet, bindet dieses Modul als Abhängigkeit ein.
### `auth-server`
Dies ist ein **eigenständiger Spring Boot Microservice**, der als Brücke zwischen dem Meldestelle-System und Keycloak agiert.
**Hauptaufgaben:**
* **Benutzer-API:** Stellt eine REST-API zur Verfügung, um Benutzer zu verwalten (z.B. Registrierung, Profil-Updates). Diese API kommuniziert im Hintergrund über den `keycloak-admin-client` mit Keycloak.
* **Token-Endpunkte:** Kann Endpunkte für den Austausch oder die Erneuerung von Tokens bereitstellen (Token Exchange).
* **Zentraler Login-Punkt (Optional):** Kann als zentraler Punkt für Login-Weiterleitungen im OAuth2/OIDC-Flow dienen.
Durch die Kapselung der Keycloak-spezifischen Logik im `auth-server` müssen die anderen Fach-Services nichts über die Interna der Benutzerverwaltung wissen. Sie interagieren nur mit der sauberen API des `auth-server`.
## Zusammenspiel im System
1. Ein **Benutzer** meldet sich über eine Client-Anwendung an und erhält ein JWT von **Keycloak**.
2. Die **Client-Anwendung** sendet eine Anfrage an einen Microservice (z.B. `masterdata-service`) und fügt das JWT als Bearer-Token in den `Authorization`-Header ein.
3. Der **Microservice**, der `auth-client` als Abhängigkeit hat, validiert das Token automatisch.
4. Wenn der Microservice Benutzerdaten ändern muss, ruft er nicht direkt Keycloak auf, sondern die sichere REST-API des **`auth-server`**.
Diese Architektur entkoppelt die Fach-Services von der Komplexität der Identitätsverwaltung und schafft eine robuste, zentrale Sicherheitsinfrastruktur.
---
**Letzte Aktualisierung**: 31. Juli 2025
@@ -1,27 +1,31 @@
// Dieses Modul enthält die clientseitige Logik für die Authentifizierung.
// Es stellt Konfigurationen und Beans bereit, um mit einem OAuth2/OIDC-Provider
// wie Keycloak zu interagieren und JWTs zu validieren.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten wie Coroutines und Logging bereit.
implementation(projects.platform.platformDependencies)
implementation(projects.core.coreDomain)
// Stellt Domänenobjekte und technische Utilities bereit.
implementation(projects.core.coreUtils)
// Spring Security für OAuth2-Client-Funktionalität und JWT-Verarbeitung.
implementation(libs.spring.boot.starter.oauth2.client)
implementation(libs.spring.boot.starter.security)
implementation(libs.spring.security.oauth2.jose)
// Bibliothek zur einfachen Handhabung von JWTs.
implementation(libs.auth0.java.jwt)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -1,33 +1,40 @@
// Dieses Modul ist ein eigenständiger Spring Boot Service, der als
// zentraler Authentifizierungs- und Autorisierungs-Server agiert.
// Er kommuniziert mit Keycloak und stellt Endpunkte für die Benutzerverwaltung bereit.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
// id("org.springframework.boot")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
// Configure main class for bootJar task
// Konfiguriert die Hauptklasse für das ausführbare JAR.
springBoot {
mainClass.set("at.mocode.infrastructure.auth.AuthServerApplicationKt")
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
implementation(projects.platform.platformDependencies)
// Nutzt die Client-Logik für die Kommunikation mit Keycloak.
implementation(projects.infrastructure.auth.authClient)
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.keycloak:keycloak-admin-client:23.0.0")
// Spring Boot Starter für einen Web-Service.
// OPTIMIERUNG: Verwendung des `spring-boot-essentials`-Bundles.
implementation(libs.bundles.spring.boot.essentials)
// Spring Security für die Absicherung des Servers.
implementation(libs.spring.boot.starter.security)
implementation(libs.spring.boot.starter.oauth2.resource.server)
// Keycloak Admin Client zur Verwaltung von Benutzern und Realms.
implementation(libs.keycloak.admin.client)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
+76
View File
@@ -0,0 +1,76 @@
# Infrastructure/Cache Module
## Überblick
Das **Cache-Modul** stellt eine zentrale und wiederverwendbare Caching-Infrastruktur für alle Microservices des Meldestelle-Systems bereit. Caching ist eine entscheidende Technik zur Verbesserung der Anwendungsleistung, zur Reduzierung der Latenz und zur Entlastung von Backend-Systemen wie der primären PostgreSQL-Datenbank.
## Architektur: Port-Adapter-Muster
Das Modul folgt streng dem **Port-Adapter-Muster** (auch als Hexagonale Architektur bekannt), um eine saubere Trennung zwischen der Caching-Schnittstelle (dem "Port") und der konkreten Implementierung (dem "Adapter") zu gewährleisten.
infrastructure/cache/
├── cache-api/ # Der "Port": Definiert die Caching-Schnittstelle
└── redis-cache/ # Der "Adapter": Implementiert die Schnittstelle mit Redis
### `cache-api`
Dieses Modul ist der **abstrakte Teil** der Architektur. Es definiert den "Vertrag" für das Caching, ohne sich um die zugrunde liegende Technologie zu kümmern.
* **Zweck:** Definiert ein oder mehrere Interfaces, z.B. `CacheService`, mit generischen Methoden wie `get(key)`, `set(key, value, ttl)` und `evict(key)`.
* **Vorteil:** Jeder Service im System programmiert nur gegen dieses Interface. Die Geschäftslogik ist vollständig von der Caching-Technologie entkoppelt. Ein Austausch des Caching-Providers (z.B. von Redis zu Caffeine) würde keine Änderungen in den Fach-Services erfordern.
### `redis-cache`
Dieses Modul ist die **konkrete Implementierung** der in `cache-api` definierten Schnittstellen.
* **Zweck:** Stellt eine Spring-basierte Konfiguration und eine Implementierung des `CacheService`-Interfaces bereit, die **Redis** als Datenspeicher verwendet. Es nutzt Spring Data Redis und den Lettuce-Client für die Kommunikation.
* **Technologie:** Verwendet Jackson für die Serialisierung der zu cachenden Objekte in das JSON-Format, bevor sie in Redis gespeichert werden.
* **Vorteil:** Kapselt die gesamte Redis-spezifische Logik an einem einzigen Ort.
## Verwendung in anderen Modulen
Ein Microservice, der Caching nutzen möchte, geht wie folgt vor:
1. **Abhängigkeit deklarieren:** Das Service-Modul (z.B. `masterdata-service`) fügt eine `implementation`-Abhängigkeit zu `:infrastructure:cache:redis-cache` in seiner `build.gradle.kts` hinzu.
```kotlin
// In masterdata-service/build.gradle.kts
dependencies {
implementation(projects.infrastructure.cache.redisCache)
}
```
2. **Interface injizieren:** Im Service-Code wird nur das Interface aus `cache-api` per Dependency Injection angefordert, nicht die konkrete Redis-Klasse.
```kotlin
// In einem Use Case oder Service
@Service
class MasterdataService(
private val cache: CacheService // Nur das Interface wird verwendet!
) {
fun findCountryById(id: String): Country? {
val cacheKey = "country:$id"
// 1. Versuche, aus dem Cache zu lesen
val cachedCountry = cache.get<Country>(cacheKey)
if (cachedCountry != null) {
return cachedCountry
}
// 2. Wenn nicht im Cache, aus der DB lesen
val dbCountry = countryRepository.findById(id)
// 3. Ergebnis in den Cache schreiben für zukünftige Anfragen
if (dbCountry != null) {
cache.set(cacheKey, dbCountry, ttl = 3600) // Cache für 1 Stunde
}
return dbCountry
}
}
```
Diese Architektur stellt sicher, dass die Geschäftslogik sauber und von Infrastrukturdetails unberührt bleibt.
---
**Letzte Aktualisierung**: 31. Juli 2025
+9 -1
View File
@@ -1,8 +1,16 @@
// Dieses Modul definiert die provider-agnostische Caching-API.
// Es enthält nur Interfaces (z.B. `CacheService`) und Datenmodelle,
// aber keine konkrete Implementierung.
plugins {
kotlin("jvm")
alias(libs.plugins.kotlin.jvm)
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten wie Logging bereit.
implementation(projects.platform.platformDependencies)
// Stellt Test-Abhängigkeiten bereit.
testImplementation(projects.platform.platformTesting)
}
+9 -13
View File
@@ -1,27 +1,23 @@
// Dieses Modul stellt eine konkrete Implementierung der `cache-api`
// unter Verwendung von Redis als Caching-Backend bereit.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
api(platform(projects.platform.platformBom))
// Implementiert die provider-agnostische Caching-API.
implementation(projects.infrastructure.cache.cacheApi)
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("io.lettuce:lettuce-core")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
// OPTIMIERUNG: Verwendung des `redis-cache`-Bundles aus libs.versions.toml.
// Dieses Bundle enthält Spring Data Redis, Lettuce und Jackson-Module.
implementation(libs.bundles.redis.cache)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
testImplementation("org.testcontainers:testcontainers")
}
@@ -0,0 +1,70 @@
# Infrastructure/Event-Store Module
## Überblick
Das **Event-Store-Modul** ist eine kritische Komponente der Infrastruktur, die für die Persistenz und Veröffentlichung von Domänen-Events zuständig ist. Es bildet die technische Grundlage für **Event Sourcing** und eine allgemeine **event-getriebene Architektur**. Anstatt nur den aktuellen Zustand einer Entität zu speichern, speichert der Event Store die gesamte Kette von Ereignissen (Events), die zu diesem Zustand geführt haben.
## Architektur: Port-Adapter-Muster
Wie schon das Cache-Modul, folgt auch der Event Store streng dem **Port-Adapter-Muster**, um eine maximale Entkopplung von der konkreten Speichertechnologie zu erreichen.
infrastructure/event-store/
├── event-store-api/ # Der "Port": Definiert die Event-Store-Schnittstelle
└── redis-event-store/ # Der "Adapter": Implementiert die Schnittstelle mit Redis Streams
### `event-store-api`
Dieses Modul ist der **abstrakte "Port"** der Architektur. Es definiert den Vertrag, wie der Rest der Anwendung mit dem Event Store interagiert.
* **Zweck:** Definiert Interfaces wie `EventStore` (zum Speichern und Laden von Event-Streams) und `EventPublisher` (zum Veröffentlichen von Events an interessierte Listener). Es ist eng mit den `DomainEvent`-Definitionen aus dem `:core:core-domain`-Modul verknüpft.
* **Vorteil:** Die Fach-Services (z.B. `members-application`) sind vollständig von der Implementierung des Event Stores entkoppelt. Sie wissen nicht, ob die Events in Redis, Kafka oder einer relationalen Datenbank gespeichert werden.
### `redis-event-store`
Dieses Modul ist der **konkrete "Adapter"**, der die in `event-store-api` definierten Schnittstellen implementiert.
* **Zweck:** Stellt eine Implementierung des `EventStore` bereit, die **Redis Streams** als zugrunde liegenden Datenspeicher verwendet. Redis Streams sind eine leistungsstarke Datenstruktur, die sich ideal für die Implementierung eines append-only Logs eignet, wie es für einen Event Store benötigt wird.
* **Technologie:** Nutzt Spring Data Redis und den Lettuce-Client für die performante Kommunikation mit Redis. Die Domänen-Events werden vor der Speicherung mittels Jackson in ein JSON-Format serialisiert.
* **Vorteil:** Kapselt die gesamte Redis-spezifische Logik. Ein zukünftiger Wechsel zu einem anderen Event-Store-System (z.B. Apache Kafka) würde nur den Austausch dieses einen Moduls erfordern.
## Verwendung in anderen Modulen
Ein Anwendungs-Service, der Event Sourcing verwendet, interagiert wie folgt mit dem Modul:
1. **Abhängigkeit deklarieren:** Das Service-Modul (z.B. `members-application`) fügt eine `implementation`-Abhängigkeit zu `:infrastructure:event-store:redis-event-store` in seiner `build.gradle.kts` hinzu.
2. **Interface injizieren:** Im Service-Code wird nur das `EventStore`-Interface aus der `event-store-api` injiziert.
```kotlin
// In einem Use Case oder Application Service
@Service
class MemberApplicationService(
private val eventStore: EventStore, // Nur das Interface wird verwendet!
private val eventPublisher: EventPublisher
) {
fun registerNewMember(command: RegisterMemberCommand): Member {
// 1. Geschäftslogik ausführen und ein oder mehrere Events erzeugen
val memberRegisteredEvent = MemberRegisteredEvent(
memberId = UUID.randomUUID(),
name = command.name,
// ...
)
// 2. Das Event im Event Store speichern
eventStore.save(memberRegisteredEvent)
// 3. Das Event veröffentlichen, damit andere Teile des Systems
// (z.B. ein E-Mail-Service) darauf reagieren können.
eventPublisher.publish(memberRegisteredEvent)
// ...
}
}
```
Diese Architektur ermöglicht eine hochgradig entkoppelte, skalierbare und resiliente Systemlandschaft, die auf asynchroner Kommunikation basiert.
---
**Letzte Aktualisierung**: 31. Juli 2025
@@ -1,13 +1,19 @@
// Dieses Modul definiert die provider-agnostische API für den Event Store.
// Es enthält die Interfaces (z.B. `EventStore`, `EventPublisher`) und die
// Domänen-Events aus `core-domain`, die gespeichert und publiziert werden.
plugins {
kotlin("jvm")
alias(libs.plugins.kotlin.jvm)
}
dependencies {
// Apply platform BOM for version management
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Abhängigkeit zu den Core-Modulen, um auf Domänenobjekte (Events)
// und technische Hilfsklassen zugreifen zu können.
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -1,33 +1,31 @@
// Dieses Modul stellt eine konkrete Implementierung der `event-store-api`
// unter Verwendung von Redis Streams als Event-Store-Backend bereit.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
// Apply platform BOM for version management
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Implementiert die provider-agnostische Event-Store-API.
implementation(projects.infrastructure.eventStore.eventStoreApi)
// Benötigt Zugriff auf Core-Module für Domänen-Events und Utilities.
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("io.lettuce:lettuce-core")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("javax.annotation:javax.annotation-api:1.3.2")
// OPTIMIERUNG: Wiederverwendung des `redis-cache`-Bundles, da es die
// gleichen Technologien (Spring Data Redis, Lettuce, Jackson) verwendet.
implementation(libs.bundles.redis.cache)
// Stellt Jakarta Annotations bereit (z.B. @PostConstruct), die von Spring verwendet werden.
implementation(libs.jakarta.annotation.api)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
testImplementation("org.testcontainers:testcontainers")
testImplementation("org.testcontainers:junit-jupiter")
// Stellt Testcontainers für Integrationstests mit einer echten Redis-Instanz bereit.
testImplementation(libs.bundles.testcontainers)
}
@@ -0,0 +1,55 @@
## Infrastructure/Gateway Module
Überblick
Das API-Gateway ist der zentrale und einzige öffentliche Einstiegspunkt für alle Anfragen von externen Clients (z.B. Web-Anwendung, Desktop-Anwendung, mobile Apps) an das Meldestelle-System. Es fungiert als "Pförtner" für die gesamte Microservice-Landschaft.
Kein externer Client sollte jemals direkt mit einem internen Microservice kommunizieren. Alle Anfragen laufen über das Gateway.
Architektur und Technologie
Das Gateway ist als eigenständiger Spring Boot Service implementiert und nutzt Spring Cloud Gateway als technologische Grundlage. Spring Cloud Gateway ist ein reaktives, nicht-blockierendes Framework, das sich nahtlos in das Spring-Ökosystem integriert.
Hauptverantwortlichkeiten
Das Gateway ist verantwortlich für die Handhabung aller Cross-Cutting Concerns (übergreifende Belange), die für mehrere oder alle Microservices gelten. Dies entlastet die Fach-Services von technischen Aufgaben.
Dynamisches Routing:
Das Gateway ist mit dem Consul Service Discovery integriert. Es fragt bei Consul an, welche Services unter welcher Adresse verfügbar sind, und leitet eingehende Anfragen dynamisch an die entsprechenden, gesunden Service-Instanzen weiter.
Beispiel: Eine Anfrage an /api/members/... wird automatisch an eine Instanz des members-service weitergeleitet.
## Sicherheit und Authentifizierung:
Das Gateway ist der Security Enforcement Point. Es bindet das :infrastructure:auth:auth-client-Modul ein, um jede eingehende Anfrage zu überprüfen:
Es validiert das im Authorization-Header mitgesendete JWT.
Anfragen ohne gültiges Token werden mit einem 401 Unauthorized-Fehler abgewiesen.
Nur validierte Anfragen mit einem gültigen Token werden an die internen Services weitergeleitet.
Rate Limiting:
Es schützt die Backend-Services vor Überlastung, indem es die Anzahl der Anfragen pro Client oder pro IP-Adresse begrenzt.
Monitoring und Tracing:
Durch die Einbindung des :infrastructure:monitoring:monitoring-client-Moduls generiert das Gateway Metriken über eingehenden Traffic und ist der Startpunkt für Distributed Traces. Jede Anfrage erhält eine eindeutige Trace-ID, die über alle folgenden Service-Aufrufe hinweg mitgeführt wird.
CORS-Management:
Verwaltet zentral die Cross-Origin Resource Sharing (CORS)-Richtlinien, um festzulegen, welche Web-Frontends auf die API zugreifen dürfen.
Zusammenspiel im System
Ein typischer Anfrage-Flow sieht wie folgt aus:
Ein Client (z.B. die Web-App) sendet eine Anfrage an https://api.meldestelle.at/members/123.
Das API-Gateway empfängt die Anfrage.
Gateway-Filter-Kette:
a. Der Security-Filter validiert das JWT.
b. Der Logging/Tracing-Filter startet einen neuen Trace.
c. Der Rate-Limiting-Filter prüft, ob das Limit überschritten ist.
Der Routing-Filter schaut in Consul nach, wo der members-service läuft (z.B. unter 172.18.0.5:8081).
Das Gateway leitet die Anfrage an die interne Service-Instanz weiter.
Die Antwort des members-service wird auf dem gleichen Weg zurück an den Client gesendet.
Diese Architektur schafft ein sicheres, robustes und wartbares System, indem sie die Komplexität der Infrastruktur vor den Fach-Services verbirgt.
Letzte Aktualisierung: 31. Juli 2025
+38 -9
View File
@@ -1,9 +1,9 @@
plugins {
/*plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ktor)
application
alias(libs.plugins.spring.dependencyManagement)
application
}
application {
@@ -12,18 +12,13 @@ application {
dependencies {
api(platform(libs.spring.boot.dependencies))
// --- Interne Module ---
// Der Gateway benötigt nur die Kern-Definitionen und Utilities.
implementation(projects.platform.platformDependencies)
implementation(projects.core.coreDomain)
implementation(projects.core.coreUtils)
// Der Gateway nutzt den Auth-Client, um Tokens zu validieren.
implementation(projects.infrastructure.auth.authClient)
implementation(projects.infrastructure.monitoring.monitoringClient)
// !!! WICHTIG: KEINE direkten Abhängigkeiten zu den Domänen- oder
// Infrastruktur-Modulen der Backend-Services mehr!
// --- Ktor Server ---
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.netty)
@@ -44,7 +39,7 @@ dependencies {
// --- Ktor Client (damit der Gateway Anfragen an die Backend-Services weiterleiten kann) ---
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.cio) // CIO ist eine gute, asynchrone Engine
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.contentNegotiation)
implementation(libs.ktor.client.serialization.kotlinx.json)
@@ -54,4 +49,38 @@ dependencies {
// --- Testing ---
testImplementation(projects.platform.platformTesting)
testImplementation(libs.ktor.server.tests)
}*/
// Dieses Modul ist das API-Gateway und der einzige öffentliche Einstiegspunkt
// für alle externen Anfragen an das Meldestelle-System.
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependencyManagement)
}
// Konfiguriert die Hauptklasse für das ausführbare JAR.
springBoot {
mainClass.set("at.mocode.infrastructure.gateway.GatewayApplicationKt")
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
implementation(projects.platform.platformDependencies)
// OPTIMIERUNG: Verwendung des `spring-cloud-gateway`-Bundles.
// Es enthält den Gateway-Starter und den Consul Discovery Client.
implementation(libs.bundles.spring.cloud.gateway)
// Bindet die wiederverwendbare Logik zur JWT-Validierung ein.
implementation(projects.infrastructure.auth.authClient)
// Bindet die wiederverwendbare Logik für Metriken und Tracing ein.
implementation(projects.infrastructure.monitoring.monitoringClient)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -0,0 +1,81 @@
# Infrastructure/Messaging Module
## Überblick
Das **Messaging-Modul** stellt die Infrastruktur für die asynchrone Kommunikation zwischen den Microservices des Meldestelle-Systems bereit. Es nutzt **Apache Kafka** als hochperformanten, verteilten Message-Broker. Dieses Modul ist entscheidend für die Entkopplung von Services und die Implementierung von Mustern wie Publish/Subscribe, um eine skalierbare und resiliente Architektur zu ermöglichen.
## Architektur
Ähnlich wie andere Infrastruktur-Module ist auch dieses in zwei spezialisierte Komponenten aufgeteilt, um Konfiguration von der Client-Logik zu trennen:
infrastructure/messaging/
├── messaging-config/ # Stellt die zentrale Kafka-Konfiguration bereit
└── messaging-client/ # Stellt wiederverwendbare Producer- und Consumer-Clients bereit
### `messaging-config`
Dieses Modul ist die Basis für jede Kafka-Interaktion. Es ist dafür verantwortlich, die gesamte **Konfiguration** zu zentralisieren.
* **Zweck:** Definiert Spring-Beans für die grundlegende Kafka-Konfiguration. Dazu gehören:
* Die Adresse der Kafka-Broker (`bootstrap-servers`).
* Konfiguration für Serializer und Deserializer (z.B. `JsonSerializer` von Spring Kafka), um sicherzustellen, dass alle Services Nachrichten im selben Format (JSON) austauschen.
* Konfiguration für Topics, Partitionen und Replikationsfaktoren.
* **Vorteil:** Jeder Service, der Kafka nutzt, kann sich auf diese zentrale Konfiguration verlassen, was die Konsistenz sicherstellt und die Einrichtung neuer Producer oder Consumer vereinfacht.
### `messaging-client`
Dieses Modul baut auf `messaging-config` auf und stellt **wiederverwendbare High-Level-Komponenten** für die Interaktion mit Kafka bereit.
* **Zweck:** Stellt einfach zu verwendende Klassen oder Services zur Verfügung, z.B. einen `KafkaProducerService` zum Senden von Nachrichten und einen `KafkaConsumerService` zum Empfangen von Nachrichten. Es nutzt **Project Reactor** (`reactor-kafka`), um eine reaktive und nicht-blockierende Verarbeitung von Nachrichten zu ermöglichen.
* **Vorteil:** Kapselt die Komplexität der Kafka-Producer- und -Consumer-API. Ein Fach-Service muss nur noch eine Methode wie `producer.sendMessage("topic", message)` aufrufen, ohne sich um die Details der Verbindung, Serialisierung oder Fehlerbehandlung kümmern zu müssen.
## Verwendung in anderen Modulen
Ein Microservice, der Nachrichten senden oder empfangen möchte, geht wie folgt vor:
1. **Abhängigkeit deklarieren:** Das Service-Modul (z.B. `events-service`) fügt eine `implementation`-Abhängigkeit zu `:infrastructure:messaging:messaging-client` in seiner `build.gradle.kts` hinzu.
2. **Client-Service injizieren:** Im Service-Code wird der `KafkaProducerService` oder `KafkaConsumerService` per Dependency Injection angefordert.
```kotlin
// Beispiel für das Senden einer Nachricht
@Service
class EventNotificationService(
private val kafkaProducer: KafkaProducerService
) {
fun notifyNewEvent(eventDetails: EventDetails) {
val topic = "new-events-topic"
// Einfacher Aufruf zum Senden der Nachricht.
// Die Komplexität der Serialisierung und des Sendens ist gekapselt.
kafkaProducer.sendMessage(topic, eventDetails.id, eventDetails)
.subscribe(
{ result -> logger.info("Message sent successfully to topic '{}'", topic) },
{ error -> logger.error("Failed to send message to topic '{}'", topic, error) }
)
}
}
```kotlin
// Beispiel für das Empfangen von Nachrichten
@Component
class EventListener(
private val kafkaConsumer: KafkaConsumerService
) {
@PostConstruct
fun listenForEvents() {
val topic = "new-events-topic"
// Reaktiv auf eingehende Nachrichten lauschen.
kafkaConsumer.receiveMessages<EventDetails>(topic)
.subscribe { event ->
logger.info("Received new event with ID: {}", event.id)
// Geschäftslogik zur Verarbeitung des Events...
}
}
}
```
Diese Architektur ermöglicht eine saubere, robuste und hochgradig entkoppelte Kommunikation zwischen den Diensten.
---
**Letzte Aktualisierung**: 31. Juli 2025
@@ -1,25 +1,23 @@
// Dieses Modul stellt High-Level-Clients (Producer/Consumer) für die
// Interaktion mit Apache Kafka bereit. Es baut auf der `messaging-config` auf.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
implementation(projects.platform.platformDependencies)
// Baut auf der zentralen Kafka-Konfiguration auf und erbt deren Abhängigkeiten.
implementation(projects.infrastructure.messaging.messagingConfig)
implementation("org.springframework.kafka:spring-kafka")
implementation("io.projectreactor.kafka:reactor-kafka")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
// Fügt die reaktive Kafka-Implementierung hinzu (Project Reactor).
implementation(libs.reactor.kafka)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -1,25 +1,23 @@
// Dieses Modul stellt die zentrale, wiederverwendbare Konfiguration
// für die Verbindung mit Apache Kafka bereit (z.B. Bootstrap-Server, Serializer).
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
implementation(projects.platform.platformDependencies)
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
api(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
api(projects.platform.platformDependencies)
implementation("org.springframework.kafka:spring-kafka")
implementation("org.springframework.boot:spring-boot-starter-json")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
// OPTIMIERUNG: Verwendung des `kafka-config`-Bundles.
// `api` wird verwendet, damit der `messaging-client` diese Konfigurationen
// und Abhängigkeiten (wie Jackson) direkt nutzen kann.
api(libs.bundles.kafka.config)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -0,0 +1,49 @@
# Infrastructure/Monitoring Module
## Überblick
Das **Monitoring-Modul** ist die Grundlage für die **Observability** (Beobachtbarkeit) der gesamten Meldestelle-Systemlandschaft. In einer verteilten Microservice-Architektur ist es unerlässlich, Einblicke in das Verhalten, die Leistung und die Gesundheit der einzelnen Dienste zu haben. Dieses Modul stellt die Werkzeuge für zwei der drei Säulen der Observability bereit: **Metriken** und **Distributed Tracing**.
* **Metriken:** Quantitative Daten über die Leistung von Services (z.B. CPU-Auslastung, Antwortzeiten, Fehlerraten).
* **Distributed Tracing:** Verfolgung einer einzelnen Anfrage über mehrere Service-Grenzen hinweg, um Engpässe und Fehlerquellen zu identifizieren.
## Architektur
Das Modul ist in eine wiederverwendbare Client-Bibliothek und einen zentralen Server aufgeteilt:
infrastructure/monitoring/
├── monitoring-client/ # Bibliothek, die jeder Service einbindet
└── monitoring-server/ # Eigenständiger Service, der den Zipkin-Server hostet
### `monitoring-client`
Dies ist eine **wiederverwendbare Bibliothek**, die von **jedem einzelnen Microservice** (z.B. `masterdata-service`, `gateway`) als Abhängigkeit eingebunden werden muss.
* **Zweck:** Instrumentiert den Service automatisch, um Metriken und Traces zu generieren.
* **Technologien:**
* **Spring Boot Actuator:** Stellt einen `/actuator/prometheus`-Endpunkt bereit, an dem Metriken im Prometheus-Format abgerufen werden können.
* **Micrometer:** Eine Fassade für Metriken, die es ermöglicht, Anwendungsmetriken zu sammeln (z.B. HTTP-Request-Zeiten, JVM-Statistiken).
* **Brave & Zipkin Reporter:** Instrumentiert den Code für Distributed Tracing und sendet die gesammelten Spans (Teile eines Traces) an den Zipkin-Server.
* **Vorteil:** Entwickler müssen sich nicht aktiv um die Implementierung von Monitoring kümmern. Durch das Einbinden dieser Bibliothek erhält jeder Service automatisch grundlegende Observability.
### `monitoring-server`
Dies ist ein **eigenständiger Spring Boot Service**, der eine zentrale Komponente des Monitoring-Stacks hostet.
* **Zweck:** Hostet den **Zipkin-Server** inklusive seiner grafischen Benutzeroberfläche. Alle `monitoring-client`-Instanzen senden ihre Tracing-Daten an diesen Server. Entwickler können dann in der Zipkin-UI die gesamten Anfrage-Flows visualisieren und analysieren.
## Zusammenspiel im Ökosystem
Das vollständige Monitoring-Setup besteht aus mehreren Teilen:
1. Jeder **Microservice** bindet `:infrastructure:monitoring:monitoring-client` ein und stellt Metriken unter `/actuator/prometheus` bereit und sendet Traces an Zipkin.
2. Der **`:infrastructure:monitoring:monitoring-server`** empfängt die Traces und stellt die Zipkin-UI zur Verfügung.
3. Ein **Prometheus-Server** (definiert in `docker-compose.yml`) ist so konfiguriert, dass er periodisch die `/actuator/prometheus`-Endpunkte aller Microservices abfragt ("scraped") und die Metriken in seiner Zeitreihen-Datenbank speichert.
4. Ein **Grafana-Server** (definiert in `docker-compose.yml`) visualisiert die in Prometheus gespeicherten Metriken in anpassbaren Dashboards.
Diese Kombination aus Micrometer, Prometheus, Zipkin und Grafana bildet einen leistungsstarken, branchenüblichen "Observability Stack".
---
**Letzte Aktualisierung**: 31. Juli 2025
@@ -1,26 +1,22 @@
// Dieses Modul ist eine wiederverwendbare Bibliothek, die von jedem Microservice
// eingebunden wird, um Metriken und Tracing-Daten zu exportieren.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
implementation(projects.platform.platformDependencies)
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
implementation("io.zipkin.reporter2:zipkin-reporter-brave")
implementation("io.zipkin.reporter2:zipkin-sender-okhttp3")
implementation("io.micrometer:micrometer-tracing-bridge-brave")
// OPTIMIERUNG: Verwendung des `monitoring-client`-Bundles.
// Es enthält Spring Boot Actuator, Micrometer Prometheus und Zipkin Tracing.
implementation(libs.bundles.monitoring.client)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}
@@ -1,33 +1,32 @@
// Dieses Modul ist ein eigenständiger Spring Boot Service, der den
// Zipkin-Server mit seiner UI hostet, um Tracing-Daten zu visualisieren.
plugins {
// kotlin("jvm")
// kotlin("plugin.spring")
// id("org.springframework.boot")
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
// KORREKTUR: Dieses Plugin ist entscheidend. Es schaltet den `springBoot`-Block
// und alle Spring-Boot-spezifischen Gradle-Tasks frei.
alias(libs.plugins.spring.boot)
// Dependency Management für konsistente Spring-Versionen
alias(libs.plugins.spring.dependencyManagement)
}
// Configure main class for bootJar task
// Konfiguriert die Hauptklasse für das ausführbare JAR.
springBoot {
mainClass.set("at.mocode.infrastructure.monitoring.MonitoringServerApplicationKt")
}
dependencies {
// Stellt sicher, dass alle Versionen aus der zentralen BOM kommen.
implementation(platform(projects.platform.platformBom))
// Stellt gemeinsame Abhängigkeiten bereit.
implementation(projects.platform.platformDependencies)
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
implementation("io.zipkin.java:zipkin-server:2.12.9")
implementation("io.zipkin.java:zipkin-autoconfigure-ui:2.12.9")
// Spring Boot Starter für einen einfachen Web-Service.
implementation(libs.spring.boot.starter.web)
implementation(libs.spring.boot.starter.actuator)
// Abhängigkeiten für den Zipkin-Server und seine UI.
// OPTIMIERUNG: Versionen werden jetzt zentral über libs.versions.toml verwaltet.
implementation(libs.zipkin.server)
implementation(libs.zipkin.autoconfigure.ui)
// Stellt alle Test-Abhängigkeiten gebündelt bereit.
testImplementation(projects.platform.platformTesting)
}