6.6 KiB
An: Senior Backend Developer
Von: Lead Software Architect
Betreff: Arbeitsauftrag: Implementierung des ping-service (Tracer Bullet)
Guten Tag,
deine nächste Aufgabe ist die Implementierung unseres ersten Microservices, des ping-service. Dieser Service ist von
strategischer Bedeutung, da er als "Tracer Bullet" und Blaupause für alle zukünftigen fachlichen Services dient.
Mit dieser Implementierung validieren wir die gesamte Kette: von der Service-Registrierung bei Consul über das Gateway-Routing und die Security mit Keycloak bis hin zur Observability mit Zipkin.
Deine Expertise in Spring Boot, DDD und sauberer Architektur ist hier entscheidend, um eine qualitativ hochwertige und wiederverwendbare Vorlage zu schaffen.
Deine Aufgaben im Detail:
- Modulstruktur anlegen
Bitte lege die folgende Modulstruktur an. Beachte die neue, klarere Benennung des Implementierungsmoduls:
:contracts:ping-api: Enthält die KMP-kompatiblen DTOs.:backend:services:ping:ping-service: Enthält die Spring Boot Anwendung, Controller und Konfiguration.
Stelle sicher, dass die Module in der settings.gradle.kts registriert sind.
include(
":platform:platform-bom",
":platform:platform-testing",
":contracts:ping-api",
":backend:services:ping:ping-service",
":backend:infrastructure:gateway",
// ":backend:services:registry:registry-api",
// ":backend:services:registry:registry-domain",
- API-Definition in
:ping-api
Definiere in ping-api/src/commonMain/kotlin ein einfaches, serialisierbares DTO. Dieses Modul darf keine
JVM-spezifischen Abhängigkeiten enthalten, um die KMP-Kompatibilität für das Frontend zu gewährleisten.
PingResponse.kt
package de.meldestelle.api.ping
import kotlinx . serialization . Serializable
@Serializable
data class PingResponse(
val message: String,
val principal: String? = null
)
- Service-Implementierung in :ping-service
Implementiere die Spring Boot Anwendung.
PingController.kt:GET /api/ping: Ein öffentlicher Endpunkt, der einePingResponse("Pong", "anonymous")zurückgibt.GET /api/ping/secure: Ein durch Spring Security geschützter Endpunkt. Er soll denpreferred_usernameaus demJwtPrincipal extrahieren und in derPingResponsezurückgeben.
Hier ist ein Implementierungsvorschlag für den Controller:
// in backend/services/ping/ping-service/src/main/kotlin/.../PingController.kt
@RestController
@RequestMapping("/api/ping")
class PingController {
@GetMapping
fun pingPublic(): PingResponse {
return PingResponse(message = "Pong", principal = "anonymous")
}
@GetMapping("/secure")
fun pingSecure(principal: Jwt): PingResponse {
val username = principal.getClaimAsString("preferred_username")
return PingResponse(message = "Pong (Secure)", principal = username)
}
}
- Konfiguration
Erstelle die application.yml für den Service. Sie muss die Anwendung für unsere Infrastruktur korrekt konfigurieren:
-
Service Name: ping-service
-
Service Discovery: Registrierung bei Consul.
-
Security: Konfiguration als Resource Server, der JWTs vom
issuer-uriunseres Keycloak-Containers validiert. -
Observability: Actuator-Endpunkte (
health,info,prometheus) freigeben und Tracing aktivieren.# in backend/services/ping/ping-service/src/main/resources/application.yml server: port: 8081 # Eindeutiger Port für den Service spring: application: name: ping-service # --- Consul Discovery --- cloud: consul: host: consul port: 8500 discovery: instance-id: \${spring.application.name}:\${random.value} health-check-path: /actuator/health health-check-interval: 10s # --- Security (Keycloak) --- security: oauth2: resourceserver: jwt: issuer-uri: http://keycloak:8080/realms/meldestelle # --- Observability --- management: endpoints: web: exposure: include: "health,info,prometheus" tracing: sampling: probability: 1.0 # Trace every request logging: pattern: level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
- **Build-Konfiguration(
build.gradle.kts)
Achte auf die korrekte und saubere Definition der Abhängigkeiten.
-
ping-api/build.gradle.ktsplugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.serialization) } kotlin { jvm() // Für die Nutzung im Backend js(IR) { browser() } // Für die Nutzung im Frontend sourceSets { commonMain.dependencies { implementation(libs.kotlinx.serialization.json) } } } -
ping-service/build.gradle.ktsplugins { alias(libs.plugins.spring.boot) alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.spring) } dependencies { // API-Modul einbinden implementation(project(":contracts:ping-api")) // Unsere zentrale BOM für konsistente Versionen implementation(platform(project(":platform:platform-bom"))) // Spring Boot Starter implementation(libs.spring.boot.starter.web) implementation(libs.spring.boot.starter.actuator) implementation(libs.spring.boot.starter.security) implementation(libs.spring.boot.starter.oauth2.resource.server) // Spring Cloud (Consul, OpenFeign etc.) implementation(libs.spring.cloud.starter.consul.discovery) // Test-Abhängigkeiten testImplementation(platform(project(":platform:platform-testing"))) testImplementation(libs.bundles.test.spring) }
Definition of Done:
Der Auftrag gilt als erledigt, wenn:
- Die Anwendung erfolgreich startet und sich im Consul UI als
UPregistriert. - Ein
GET-Request aufhttp//localhost:8081/api/ping(über das Gateway) den Status200 OKund die{"message":"Pong", "principal":"anonymous"}zurückgibt. - Ein
GET-Request aufhttp//localhost:8081/api/ping/secureohne Token den Status401 Unauthorizedzurückgibt. - Ein
GET-Request aufhttp//localhost:8081/api/ping/securemit einem gültigen Keycloak-Token deb Status200 OKund eine Antwort mit dem korrekten Benutzernamen zurückgibt. - Die Requests in der Zipkin UI als Trace sichtbar sind.
Bei Fragen zur Konfiguration oder zur Architektur stehe ich dir zur Verfügung.