Diese Seite bleibt auch ohne JavaScript lesbar. Suche und Buttons sind Zusatzkomfort.
Spring Cloud Stream und Spring Integration
Events, Binder, Enterprise Integration Patterns, Idempotenz und Dead Letter.
1. Event-driven heißt fachlich denken
Ein Event ist nicht nur eine technische Nachricht. Es beschreibt eine fachliche Tatsache: OrderSubmitted, PaymentCaptured, ShipmentPrepared. Wenn Events nur als technische DTOs ohne Bedeutung gebaut werden, entstehen schwer wartbare Integrationsketten.
Spring Cloud Stream abstrahiert Broker über Binder. Dadurch kann Anwendungscode stärker auf Funktionen und Nachrichten fokussieren, während Kafka, RabbitMQ oder andere Systeme über Konfiguration eingebunden werden.
@Configuration
class OrderEventHandlers {
@Bean
Consumer<OrderSubmittedEvent> reserveStock(InventoryService inventory) {
return event -> inventory.reserve(event.orderId(), event.lines());
}
}
spring:
cloud:
function:
definition: reserveStock
stream:
bindings:
reserveStock-in-0:
destination: order-submitted
group: inventory-service
kafka:
bindings:
reserveStock-in-0:
consumer:
enableDlq: true
2. Idempotenz und At-least-once
Viele Broker liefern praktisch mindestens einmal. Das heißt: ein Consumer muss Duplikate verkraften. Eine fachliche Operation wie Reservierung, Buchung oder Benachrichtigung braucht einen Idempotenzschlüssel und eine klare Wiederholungsstrategie.
@Transactional
public void reserve(OrderSubmittedEvent event) {
if (processedEvents.existsById(event.eventId())) {
return;
}
inventory.reserve(event.orderId(), event.lines());
processedEvents.save(new ProcessedEvent(event.eventId(), Instant.now()));
}
3. Spring Integration und EIP
Spring Integration ist stark, wenn Nachrichten zwischen Systemen transformiert, geroutet, gesplittet, aggregiert oder über Adapter mit Dateien, Mail, FTP, JMS, JDBC oder anderen Kanälen verbunden werden. Es ist besonders für Integrationslandschaften nützlich, nicht für jede kleine interne Methode.
@Bean
IntegrationFlow inboundFileFlow(OrderImportService importer) {
return IntegrationFlow
.from(Files.inboundAdapter(new File("/imports/orders")), e -> e.poller(Pollers.fixedDelay(5000)))
.filter(file -> ((File) file).getName().endsWith(".csv"))
.transform(new CsvToOrderCommandTransformer())
.handle(importer, "importOrder")
.get();
}
Enterprise-Prüffragen
- Events fachlich benannt?
- Idempotenz pro Consumer geklärt?
- DLQ und Retry bewusst konfiguriert?
- IntegrationFlow als Adapter statt Fachlogik-Monster?