Der Unterschied zwischen Event-driven und Message-driven Ansätzen ist fundamental für das Verständnis moderner Architekturparadigmen. Obwohl beide Konzepte asynchrone Kommunikation verwenden, führen sie zu völlig unterschiedlichen Systemdesigns und Verantwortlichkeiten.
Die grundlegende Denkweise unterscheidet sich fundamental: Events sind unveränderliche Fakten über etwas, das bereits geschehen ist, während Messages Anweisungen für etwas sind, das geschehen soll.
Die verwendete Sprache verrät oft den jeweiligen Ansatz:
Event-driven:
Message-driven:
Bei Events liegt die Definitionsmacht beim Producer. Ein OrderService definiert die Struktur seines OrderPlaced-Events basierend auf seinen internen Geschäftsregeln. Consumer müssen sich an diese Struktur anpassen.
Bei Messages liegt die Definitionsmacht beim Consumer. Ein PaymentService definiert, wie ein ProcessPayment-Command strukturiert sein muss, damit er ihn verarbeiten kann.
// Event-driven: Producer definiert Struktur
public class OrderService {
public void placeOrder(Order order) {
// OrderService entscheidet, welche Daten relevant sind
OrderPlacedEvent event = new OrderPlacedEvent(
order.getId(),
order.getCustomerId(),
order.getItems(),
order.getTotalAmount(),
order.getOrderDate()
);
publishEvent(event);
}
}
// Message-driven: Consumer definiert Struktur
public class PaymentService {
// PaymentService definiert, was er für ProcessPayment braucht
@MessageHandler
public void handle(ProcessPaymentCommand command) {
// Command muss paymentMethod, amount, orderId enthalten
processPayment(command.getPaymentMethod(),
command.getAmount(),
command.getOrderId());
}
}# Event-driven: Producer definiert Struktur
class OrderService:
def place_order(self, order):
# OrderService entscheidet, welche Daten relevant sind
event = {
'eventType': 'OrderPlaced',
'orderId': order['id'],
'customerId': order['customerId'],
'items': order['items'],
'totalAmount': order['totalAmount'],
'orderDate': order['orderDate']
}
self.publish_event(event)
# Message-driven: Consumer definiert Struktur
class PaymentService:
def handle_process_payment(self, command):
# PaymentService definiert Command-Struktur
self.process_payment(
command['paymentMethod'],
command['amount'],
command['orderId']
)Event-driven Systeme behandeln Fehler defensiv. Wenn ein Consumer ein Event nicht verarbeiten kann, ist das sein Problem – das Event bleibt bestehen und kann später erneut verarbeitet werden.
Message-driven Systeme erfordern oft koordinierte Fehlerbehandlung zwischen Sender und Empfänger, da der Sender ein Ergebnis erwartet.
Events entwickeln sich typischerweise additiv. Neue Felder werden hinzugefügt, aber bestehende bleiben erhalten, um Kompatibilität zu gewährleisten:
// OrderPlaced Event v1
{
"eventType": "OrderPlaced",
"version": "v1",
"orderId": "123",
"customerId": "456"
}
// OrderPlaced Event v2 (additiv)
{
"eventType": "OrderPlaced",
"version": "v2",
"orderId": "123",
"customerId": "456",
"customerTier": "premium",
"promotionCode": "SAVE10"
}Messages hingegen entwickeln sich oft disruptiv, da sie spezifische Aktionen auslösen und Änderungen direktere Auswirkungen haben.
Rich Events enthalten alle relevanten Informationen zum Zeitpunkt des Ereignisses:
// Rich Event - enthält alle relevanten Daten
public class OrderPlacedEvent {
private String orderId;
private CustomerInfo customer; // Vollständige Kundeninformation
private List<OrderItem> items; // Alle Bestellpositionen
private Address shippingAddress; // Lieferadresse
private PaymentInfo paymentInfo; // Zahlungsinformationen
private BigDecimal totalAmount;
}Thin Events enthalten nur Referenzen und grundlegende Informationen:
// Thin Event - nur Referenzen
public class OrderPlacedEvent {
private String orderId;
private String customerId;
private BigDecimal totalAmount;
// Consumer müssen weitere Daten bei Bedarf abrufen
}Empfehlung: Verwenden Sie Rich Events für kritische Geschäftsereignisse, um Consumer-Autonomie zu fördern und Round-Trips zu vermeiden.
Events sollten eine einzelne Geschäftseinheit repräsentieren:
// Gut: Ein Event pro Geschäftsereignis
OrderPlacedEvent, PaymentProcessedEvent, OrderShippedEvent
// Vermeiden: Zu generische Events
OrderChangedEvent, OrderUpdatedEvent, DataChangedEventUnterscheiden Sie zwischen Domain Events (innerhalb einer Bounded Context) und Integration Events (zwischen Bounded Contexts):
// Domain Event (intern im Order-Service)
class OrderValidationCompleted {
private String orderId;
private boolean isValid;
private List<ValidationError> errors;
}
// Integration Event (zwischen Services)
class OrderPlaced {
private String orderId;
private String customerId;
private BigDecimal totalAmount;
// Nur öffentlich relevante Informationen
}Event-driven Thinking ermöglicht lose gekoppelte, skalierbare Systeme, erfordert aber ein Umdenken von imperativer zu deklarativer Kommunikation. Das Verständnis dieser konzeptionellen Unterschiede ist entscheidend für den Erfolg einer Event-Driven Architecture.