This commit introduces a comprehensive refactoring of the messaging module to establish a fully reactive, non-blocking, and robust infrastructure for Kafka-based communication. Features & Refinements Reactive Publisher: The KafkaEventPublisher has been refactored from a blocking implementation (KafkaTemplate) to a fully non-blocking, reactive one using Spring's ReactiveKafkaProducerTemplate. The EventPublisher interface now returns reactive types (Mono, Flux) to reflect the asynchronous nature of the operations. Reactive Consumer: A new KafkaEventConsumer has been implemented, providing a standardized, reusable, and reactive way for services to consume events. It encapsulates the complexity of reactor-kafka and exposes a simple receiveEvents<T>(topic) method that returns a Flux<T>. Architectural Cleanup: The Spring configuration has been split. The basic ProducerFactory and consumer properties reside in messaging-config, while the reactive-specific ReactiveKafkaProducerTemplate bean is now correctly located in messaging-client. Testing Added Kafka Integration Test: A new KafkaIntegrationTest has been created to ensure the reliability of the messaging infrastructure. The test uses Testcontainers to spin up a real Apache Kafka broker for end-to-end validation. Project Reactor's StepVerifier is used to test the reactive streams deterministically, avoiding flaky tests. The test correctly manages the lifecycle of Kafka producers to ensure clean shutdown without hanging threads. Bug Fixes Resolved UninitializedPropertyAccessException in tests by making the KafkaConfig test-friendly. Fixed IllegalStateException related to Testcontainers lifecycle by making the container a static resource. Corrected compilation errors in tests related to resource cleanup by using the concrete DefaultKafkaProducerFactory type.
3.9 KiB
Infrastructure/Messaging Module
Überblick
Das Messaging-Modul stellt die Infrastruktur für die asynchrone, reaktive Kommunikation zwischen den Microservices bereit. Es nutzt Apache Kafka als hochperformanten, verteilten Message-Broker und ist entscheidend für die Entkopplung von Services und die Implementierung einer skalierbaren, ereignisgesteuerten Architektur.
Architektur
Das Modul ist 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, reaktive Clients bereit
messaging-config
Dieses Modul zentralisiert die grundlegende Kafka-Konfiguration für das gesamte Projekt.
- Zweck: Definiert Spring-Beans für die
ProducerFactory(Basis für Producer) und eineMapmit Standard-Konfigurationen für Consumer (z.B.bootstrap-servers,group-id, Serializer). - Vorteil: Stellt Konsistenz sicher und vereinfacht die Einrichtung neuer Producer oder Consumer in den Services.
messaging-client
Dieses Modul baut auf der Konfiguration auf und stellt wiederverwendbare High-Level-Komponenten für die Interaktion mit Kafka bereit.
- Zweck:
KafkaEventPublisher: Ein reaktiver, nicht-blockierender Service zum Senden von Nachrichten. Er nutzt denReactiveKafkaProducerTemplatevon Spring.KafkaEventConsumer: Ein reaktiver Service zum Empfangen von Nachrichten. Er kapselt die Komplexität vonreactor-kafkaund gibt einen kontinuierlichenFlux-Stream von Events zurück.
- Vorteil: Kapselt die Komplexität der reaktiven Kafka-API. Ein Fach-Service muss nur noch reaktive Streams (
Mono,Flux) handhaben, ohne sich um die Details der Kafka-Interaktion zu kümmern.
Verwendung
Ein Microservice, der Nachrichten senden oder empfangen möchte, deklariert eine Abhängigkeit zu :infrastructure:messaging:messaging-client und injiziert die entsprechenden Interfaces.
Beispiel für das Senden einer Nachricht (nicht-blockierend):
@Service
class EventNotificationService(
private val eventPublisher: EventPublisher
) {
fun notifyNewEvent(eventDetails: EventDetails) {
val topic = "new-events-topic"
eventPublisher.publishEvent(topic, eventDetails.id, eventDetails)
.subscribe(
null, // onComplete: Nichts zu tun
{ error -> logger.error("Failed to send message to topic '{}'", topic, error) }
)
// Die Methode kehrt sofort zurück, ohne auf die Bestätigung von Kafka zu warten.
}
}
Beispiel für das Empfangen von Nachrichten (reaktiv):
@Component
class EventListener(
private val eventConsumer: EventConsumer
) {
@PostConstruct
fun listenForEvents() {
val topic = "new-events-topic"
eventConsumer.receiveEvents<EventDetails>(topic)
.subscribe { event ->
logger.info("Received new event with ID: {}", event.id)
// Geschäftslogik zur Verarbeitung des Events...
}
}
}
Testing-Strategie
Die Zuverlässigkeit des Moduls wird durch einen umfassenden Integrationstest sichergestellt, der auf dem "Goldstandard"-Prinzip beruht:
-
*Testcontainers: Der KafkaIntegrationTest startet einen echten Apachen Kafka Docker-Container, um die Funktionalität unter realen Bedingungen zu validieren.
-
*Reaktives Testen: Der Test nutzt Project Reactor's StepVerifier, um die reaktiven Streams (Mono, Flux) deterministisch und ohne unzuverlässige Thread.sleep-Aufrufe zu überprüfen.
-
*Lifecycle Management: Der Test-Lebenszyklus wird sauber über @BeforeEach und @AfterEach verwaltet, um sicherzustellen, dass alle Ressourcen (insbesondere Producer-Threads) nach jedem Test korrekt freigegeben werden.
Letzte Aktualisierung: 9. August 2025