16 Semantik und Granularität von Events

Die Bedeutung und der Detailgrad von Events beeinflussen maßgeblich die Flexibilität und Wartbarkeit event-getriebener Systeme. Während technische Korrektheit eine Grundvoraussetzung ist, entscheidet die semantische Klarheit über die langfristige Entwickelbarkeit und Verständlichkeit der Architektur.

16.1 Single Responsibility für Events

Das Single Responsibility Principle gilt nicht nur für Klassen und Module, sondern auch für Events. Ein Event sollte genau ein geschäftlich relevantes Ereignis repräsentieren und alle dafür notwendigen, aber nur diese Informationen enthalten.

Verletzung des Single Responsibility:

// Spring Boot - Anti-Pattern: Zu viele Verantwortlichkeiten
public class OrderProcessingEvent {
    // Bestellung
    private String orderId;
    private List<OrderItem> items;
    private BigDecimal totalAmount;
    
    // Zahlung
    private String paymentMethod;
    private String transactionId;
    private PaymentStatus paymentStatus;
    
    // Versand
    private String shippingAddress;
    private String carrierService;
    private LocalDate estimatedDelivery;
    
    // Inventar
    private Map<String, Integer> reservedQuantities;
    private List<String> unavailableItems;
}

Korrekte Single Responsibility:

// Spring Boot - Focused Events mit klarer Verantwortung
public class OrderEvents {
    
    public static class OrderPlaced {
        private String orderId;
        private String customerId;
        private List<OrderItem> items;
        private BigDecimal totalAmount;
        private Instant placedAt;
        // Nur bestellungsrelevante Daten
    }
    
    public static class PaymentProcessed {
        private String orderId;
        private String paymentId;
        private String paymentMethod;
        private BigDecimal amount;
        private PaymentStatus status;
        // Nur zahlungsrelevante Daten
    }
    
    public static class InventoryReserved {
        private String orderId;
        private Map<String, Integer> reservedItems;
        private String reservationId;
        private Instant reservedUntil;
        // Nur inventarrelevante Daten
    }
}
# Python - Entsprechende fokussierte Events
from dataclasses import dataclass
from typing import Dict, List
from datetime import datetime
from decimal import Decimal

@dataclass(frozen=True)
class OrderPlaced:
    """Event für reine Bestellinformationen"""
    order_id: str
    customer_id: str
    items: List[dict]
    total_amount: Decimal
    placed_at: datetime

@dataclass(frozen=True)  
class PaymentProcessed:
    """Event für reine Zahlungsinformationen"""
    order_id: str
    payment_id: str
    payment_method: str
    amount: Decimal
    status: str

@dataclass(frozen=True)
class InventoryReserved:
    """Event für reine Inventarinformationen"""
    order_id: str
    reserved_items: Dict[str, int]
    reservation_id: str
    reserved_until: datetime

Single Responsibility bei Events ermöglicht:

16.2 Event-Sourcing vs. State-Transfer Events

Event-Sourcing und State-Transfer repräsentieren zwei grundlegend verschiedene Philosophien für die Event-Modellierung mit jeweils eigenen Vor- und Nachteilen.

Event-Sourcing Events dokumentieren die Intention oder Aktion, die zu einer Zustandsänderung geführt hat:

// Spring Boot - Event-Sourcing Stil
public class OrderEventSourcing {
    
    public static class OrderItemAdded {
        private String orderId;
        private String productId;
        private Integer quantity;
        private BigDecimal unitPrice;
        private Instant addedAt;
        // Dokumentiert die AKTION des Hinzufügens
    }
    
    public static class OrderItemQuantityChanged {
        private String orderId;
        private String productId;
        private Integer oldQuantity;
        private Integer newQuantity;
        private String changeReason;
        // Dokumentiert die ÄNDERUNG der Menge
    }
    
    public static class OrderSubmitted {
        private String orderId;
        private String submittedBy;
        private Instant submittedAt;
        // Dokumentiert die AKTION des Absendens
    }
}

State-Transfer Events übertragen den aktuellen Zustand nach einer Änderung:

// Spring Boot - State-Transfer Stil  
public class OrderStateTransfer {
    
    public static class OrderStateChanged {
        private String orderId;
        private OrderState currentState;
        private List<OrderItem> currentItems;
        private BigDecimal currentTotal;
        private Instant lastModified;
        // Überträgt den ZUSTAND nach Änderung
    }
    
    public static class OrderSnapshot {
        private String orderId;
        private String customerId;
        private OrderStatus status;
        private List<OrderItem> items;
        private BigDecimal totalAmount;
        private PaymentInfo paymentInfo;
        private ShippingInfo shippingInfo;
        // Vollständiger aktueller ZUSTAND
    }
}
# Python - Vergleich beider Ansätze
@dataclass(frozen=True)
class OrderItemAdded:  # Event-Sourcing
    """Dokumentiert WAS passiert ist"""
    order_id: str
    product_id: str
    quantity: int
    unit_price: Decimal
    added_at: datetime

@dataclass(frozen=True)
class OrderUpdated:  # State-Transfer
    """Überträgt WIE es jetzt ist"""
    order_id: str
    current_status: str
    current_items: List[dict]
    current_total: Decimal
    last_modified: datetime
Aspekt Event-Sourcing State-Transfer
Information Intention/Aktion Aktueller Zustand
Historisierung Vollständig möglich Nur aktueller Stand
Replay-Fähigkeit Ja, alle Schritte Nein, nur Endstand
Komplexität Höher (Rekonstruktion) Niedriger (direkter Zustand)
Auditierbarkeit Exzellent Begrenzt
Performance Langsamer (Aggregation) Schneller (direkter Zugriff)

16.3 Choreography-freundliche Event-Designs

Choreography-basierte Systeme koordinieren sich durch Event-Austausch ohne zentrale Steuerung. Events müssen daher so designed werden, dass sie autonome Entscheidungen ermöglichen.

Choreography-unfreundlich - Events mit wenig Kontext:

// Spring Boot - Schlechtes Choreography-Design
public class PoorChoreographyEvents {
    
    public static class OrderStatusChanged {
        private String orderId;
        private String newStatus; // Zu wenig Kontext!
        // Consumer wissen nicht, WARUM sich der Status änderte
    }
    
    public static class ProcessNext {
        private String orderId;
        // Zu generisch - was soll "next" sein?
    }
}

Choreography-freundlich - Events mit ausreichendem Kontext:

// Spring Boot - Choreography-optimierte Events
public class ChoreographyFriendlyEvents {
    
    public static class OrderPaymentConfirmed {
        private String orderId;
        private String paymentId;
        private BigDecimal paidAmount;
        private String paymentMethod;
        private Instant confirmedAt;
        private List<OrderItem> paidItems;
        // Vollständiger Kontext für Folgeaktionen
    }
    
    public static class OrderReadyForShipping {
        private String orderId;
        private String customerId;
        private ShippingAddress destination;
        private List<ShippableItem> items;
        private ShippingPreferences preferences;
        private Instant readyAt;
        // Alle Infos für autonome Versandentscheidung
    }
    
    public static class InventoryBelowThreshold {
        private String productId;
        private Integer currentStock;
        private Integer thresholdLevel;
        private Integer recommendedReorderQuantity;
        private List<String> affectedOrders;
        // Kontext für autonome Nachbestellentscheidung
    }
}
# Python - Choreography-optimierte Event-Designs
@dataclass(frozen=True)
class OrderPaymentConfirmed:
    """Ermöglicht autonome Folgeentscheidungen"""
    order_id: str
    payment_id: str
    paid_amount: Decimal
    payment_method: str
    confirmed_at: datetime
    paid_items: List[dict]
    
    def enables_shipping(self) -> bool:
        """Consumer kann autonom entscheiden"""
        return self.paid_amount > 0 and len(self.paid_items) > 0

@dataclass(frozen=True)
class CustomerEligibilityChanged:
    """Vollständiger Kontext für Berechtigungsentscheidungen"""
    customer_id: str
    previous_tier: str
    new_tier: str
    tier_benefits: List[str]
    change_reason: str
    effective_date: datetime
    
    def can_access_premium_features(self) -> bool:
        """Autonome Berechtigungsprüfung möglich"""
        return self.new_tier in ['premium', 'vip']

Gestaltungsprinzipien für Choreography:

// Spring Boot - Event-Design-Patterns für Choreography
public class ChoreographyPatterns {
    
    // Pattern 1: Event mit Entscheidungskontext
    public static class OrderEvaluated {
        private String orderId;
        private Boolean fraudRiskDetected;
        private BigDecimal riskScore;
        private List<String> riskFactors;
        private Boolean requiresManualReview;
        // Ermöglicht autonome Behandlungsentscheidung
    }
    
    // Pattern 2: Event mit Aktionsvorschlägen
    public static class CustomerBehaviorAnalyzed {
        private String customerId;
        private CustomerSegment currentSegment;
        private List<RecommendedAction> suggestedActions;
        private Map<String, Double> behaviorScores;
        // Gibt Handlungsempfehlungen mit
    }
    
    // Pattern 3: Event mit Kompensationsinfo
    public static class PaymentFailed {
        private String orderId;
        private String paymentId;
        private String failureReason;
        private Boolean retryable;
        private Integer maxRetryAttempts;
        private Duration retryDelay;
        // Ermöglicht autonome Wiederholungsstrategie
    }
}

16.4 Praktische Anwendung der Semantik-Prinzipien

Die semantische Gestaltung von Events manifestiert sich in konkreten Design-Entscheidungen:

// Spring Boot - Vollständiges Beispiel semantisch optimierter Events
@Component
public class SemanticEventExamples {
    
    // Single Responsibility: Ein Event = Ein Geschäftsereignis
    public static class CustomerSubscribedToNewsletter {
        private String customerId;
        private String newsletterType;
        private String subscriptionSource;
        private Instant subscribedAt;
        private Map<String, String> preferences;
    }
    
    // Event-Sourcing: Dokumentiert Intention
    public static class CustomerNewsletterPreferenceChanged {
        private String customerId;
        private String newsletterType;
        private String oldFrequency;
        private String newFrequency;
        private String changeReason;
        private Instant changedAt;
    }
    
    // Choreography: Vollständiger Entscheidungskontext
    public static class CustomerNewsletterEngagementEvaluated {
        private String customerId;
        private String newsletterType;
        private Double engagementScore;
        private Integer consecutiveUnopened;
        private Boolean recommendUnsubscribe;
        private List<String> alternativeNewsletters;
        private Instant evaluatedAt;
    }
}
# Python - Semantische Event-Design-Patterns
from abc import ABC, abstractmethod
from enum import Enum

class EventSemantic(Enum):
    SINGLE_RESPONSIBILITY = "single_responsibility"
    EVENT_SOURCING = "event_sourcing"
    CHOREOGRAPHY_FRIENDLY = "choreography_friendly"

class SemanticEvent(ABC):
    """Basis für semantisch klare Events"""
    
    @abstractmethod
    def semantic_type(self) -> EventSemantic:
        pass
    
    @abstractmethod
    def business_meaning(self) -> str:
        pass

@dataclass(frozen=True)
class ProductReviewSubmitted(SemanticEvent):
    """Einzelne Verantwortlichkeit: Review-Ereignis"""
    product_id: str
    customer_id: str
    rating: int
    review_text: str
    submitted_at: datetime
    
    def semantic_type(self) -> EventSemantic:
        return EventSemantic.SINGLE_RESPONSIBILITY
    
    def business_meaning(self) -> str:
        return f"Customer {self.customer_id} reviewed product {self.product_id}"

@dataclass(frozen=True)
class ProductRatingRecalculated(SemanticEvent):
    """Choreography-freundlich: Vollständiger Kontext"""
    product_id: str
    previous_average_rating: float
    new_average_rating: float
    total_reviews: int
    rating_distribution: Dict[int, int]
    significant_change: bool
    recalculated_at: datetime
    
    def semantic_type(self) -> EventSemantic:
        return EventSemantic.CHOREOGRAPHY_FRIENDLY
    
    def business_meaning(self) -> str:
        return f"Product {self.product_id} rating changed from {self.previous_average_rating} to {self.new_average_rating}"

Die semantische Gestaltung von Events erfordert bewusste Designentscheidungen, die die zukünftige Evolution und Flexibilität des Systems maßgeblich beeinflussen. Single Responsibility sorgt für Klarheit, die Wahl zwischen Event-Sourcing und State-Transfer beeinflusst Auditierbarkeit und Performance, während Choreography-freundliche Designs autonome Systemkoordination ermöglichen.