19 Event-Namen, Kontrakte und Bounded Contexts

Event-Namen sind mehr als technische Bezeichner – sie transportieren Geschäftslogik, definieren Systemgrenzen und ermöglichen oder verhindern Verständlichkeit. Durchdachte Namenskonventionen und Kontraktdesign schaffen Klarheit in komplexen Systemlandschaften, während schlecht benannte Events zu Missverständnissen und Integrationsproblemen führen.

19.1 Naming Conventions und Standards

Konsistente Event-Benennung folgt erkennbaren Mustern, die sowohl die Geschäftssemantik als auch die technische Handhabung unterstützen. Event-Namen sollten selbsterklärend sein und die Ubiquitous Language der jeweiligen Domäne widerspiegeln.

Grundlegende Naming-Patterns:

// Spring Boot - Konsistente Event-Naming-Patterns
public class ECommerceEventNaming {
    
    // Pattern 1: Domain + BusinessAction + PastTense
    public static class OrderPlaced {        // Order-Domain, Place-Action, Past-Tense
        private String orderId;
        private String customerId;
        // Event dokumentiert abgeschlossene Aktion
    }
    
    public static class PaymentProcessed {   // Payment-Domain, Process-Action, Past-Tense
        private String paymentId;
        private String orderId;
        // Vergangenheitsform zeigt vollendete Tatsache
    }
    
    public static class InventoryReserved {  // Inventory-Domain, Reserve-Action, Past-Tense
        private String productId;
        private Integer quantity;
        // Klare Geschäftssprache
    }
    
    // Pattern 2: BusinessEntity + StateChange
    public static class CustomerActivated {  // Customer-Entity, Activated-State
        private String customerId;
        private Instant activatedAt;
        // Zustandsänderung explizit
    }
    
    public static class ProductDiscontinued { // Product-Entity, Discontinued-State
        private String productId;
        private String reason;
        // Geschäftsereignis klar benannt
    }
    
    // Pattern 3: BusinessProcess + Milestone
    public static class ShippingPrepared {   // Shipping-Process, Prepared-Milestone
        private String orderId;
        private String shippingCarrier;
        // Prozess-Fortschritt dokumentiert
    }
    
    public static class OrderFulfillmentCompleted { // OrderFulfillment-Process, Completed-Milestone
        private String orderId;
        private Instant completedAt;
        // End-to-End-Prozess abgeschlossen
    }
}

Anti-Patterns vermeiden:

// Spring Boot - Event-Naming Anti-Patterns
public class EventNamingAntiPatterns {
    
    // ❌ Zu generisch
    public static class DataChanged {        // Welche Daten? Wie geändert?
        private String entityId;
        private String changeType;
    }
    
    // ❌ Technisch statt geschäftlich
    public static class DatabaseRecordInserted { // Implementierungsdetail
        private String tableName;
        private String recordId;
    }
    
    // ❌ Unklare Zeitform
    public static class ProcessOrder {       // Command, nicht Event
        private String orderId;
    }
    
    // ❌ Domänen-übergreifend
    public static class CustomerOrderPaymentShippingUpdate { // Zu viele Verantwortungen
        private String customerId;
        private String orderId;
        private String paymentStatus;
        private String shippingStatus;
    }
    
    // ✅ Bessere Alternativen:
    public static class CustomerProfileUpdated {
        private String customerId;
        private CustomerProfile updatedProfile;
        // Spezifisch und geschäftlich
    }
    
    public static class OrderStatusChanged {
        private String orderId;
        private OrderStatus previousStatus;
        private OrderStatus newStatus;
        // Klare Geschäftslogik
    }
}
# Python - Event-Naming-Konventionen mit Validierung
from dataclasses import dataclass
from typing import Pattern, List
import re
from enum import Enum

class EventNamingPattern(Enum):
    DOMAIN_ACTION_PAST = "domain_action_past"           # OrderPlaced
    ENTITY_STATE_CHANGE = "entity_state_change"         # CustomerActivated  
    PROCESS_MILESTONE = "process_milestone"             # ShippingPrepared
    BUSINESS_FACT = "business_fact"                     # PaymentDeclined

class EventNameValidator:
    """Validiert Event-Namen gegen Naming-Conventions"""
    
    def __init__(self):
        # Regex-Patterns für verschiedene Naming-Konventionen
        self.patterns = {
            EventNamingPattern.DOMAIN_ACTION_PAST: re.compile(
                r'^[A-Z][a-zA-Z]+[A-Z][a-zA-Z]+(ed|d)$'  # OrderPlaced, PaymentProcessed
            ),
            EventNamingPattern.ENTITY_STATE_CHANGE: re.compile(
                r'^[A-Z][a-zA-Z]+[A-Z][a-zA-Z]+$'        # CustomerActivated, ProductDiscontinued
            ),
            EventNamingPattern.PROCESS_MILESTONE: re.compile(
                r'^[A-Z][a-zA-Z]+(Prepared|Completed|Started|Failed)$'  # ShippingPrepared
            )
        }
        
        # Verbotene Wörter (zu generisch oder technisch)
        self.forbidden_words = {
            'data', 'record', 'entity', 'object', 'item', 'thing',
            'updated', 'changed', 'modified', 'processed'
        }
    
    def validate_event_name(self, event_name: str) -> List[str]:
        """Validiert Event-Name und gibt Verbesserungsvorschläge"""
        issues = []
        
        # Prüfe auf verbotene Wörter
        lower_name = event_name.lower()
        for forbidden in self.forbidden_words:
            if forbidden in lower_name:
                issues.append(f"Avoid generic word '{forbidden}' - be more specific")
        
        # Prüfe Pattern-Konformität
        matches_pattern = any(
            pattern.match(event_name) 
            for pattern in self.patterns.values()
        )
        
        if not matches_pattern:
            issues.append("Name doesn't follow standard patterns (DomainActionPast, EntityStateChange, ProcessMilestone)")
        
        # Prüfe Geschäftssprache
        if any(tech_word in lower_name for tech_word in ['database', 'table', 'record', 'insert', 'update']):
            issues.append("Use business language, not technical implementation details")
        
        return issues

# Korrekte Event-Namen mit Pattern-Zuordnung
@dataclass
class WellNamedEvents:
    """Beispiele für korrekt benannte Events"""
    
    # Domain + Action + Past Tense
    order_placed: str = "OrderPlaced"
    payment_processed: str = "PaymentProcessed"
    inventory_reserved: str = "InventoryReserved"
    
    # Entity + State Change
    customer_activated: str = "CustomerActivated"
    product_discontinued: str = "ProductDiscontinued"
    subscription_renewed: str = "SubscriptionRenewed"
    
    # Process + Milestone
    shipping_prepared: str = "ShippingPrepared"
    fulfillment_completed: str = "FulfillmentCompleted"
    quality_check_passed: str = "QualityCheckPassed"
    
    def get_pattern(self, event_name: str) -> EventNamingPattern:
        """Ermittelt das Naming-Pattern eines Events"""
        validator = EventNameValidator()
        
        for pattern_type, regex in validator.patterns.items():
            if regex.match(event_name):
                return pattern_type
        
        raise ValueError(f"Event name '{event_name}' doesn't match any standard pattern")

Namespace- und Versionierungs-Konventionen:

// Spring Boot - Namespace und Versionierung
public class EventNamespacing {
    
    // Hierarchische Namespaces
    public static final String DOMAIN_PREFIX = "de.eda.training";
    
    // Domain-spezifische Namespaces
    public static class OrderDomain {
        public static final String NAMESPACE = DOMAIN_PREFIX + ".order";
        
        @JsonTypeName("OrderPlaced")
        public static class OrderPlaced {
            @JsonProperty("@type")
            private String type = NAMESPACE + ".OrderPlaced";
            
            @JsonProperty("@version")
            private String version = "1.0";
            
            // Event-Daten...
        }
    }
    
    public static class PaymentDomain {
        public static final String NAMESPACE = DOMAIN_PREFIX + ".payment";
        
        @JsonTypeName("PaymentProcessed")
        public static class PaymentProcessed {
            @JsonProperty("@type") 
            private String type = NAMESPACE + ".PaymentProcessed";
            
            @JsonProperty("@version")
            private String version = "2.1";
            
            // Event-Daten...
        }
    }
    
    // Topic-Naming mit Konventionen
    public static class TopicNaming {
        // Pattern: domain.event.version
        public static final String ORDER_PLACED_V1 = "order.placed.v1";
        public static final String ORDER_PLACED_V2 = "order.placed.v2";
        public static final String PAYMENT_PROCESSED_V2 = "payment.processed.v2";
        
        // Environment-spezifische Topics
        public static String getEnvironmentTopic(String baseTopic, String environment) {
            return environment + "." + baseTopic;  // prod.order.placed.v1
        }
    }
}

19.2 API-First Design für Events

API-First Design behandelt Events als APIs mit definierten Kontrakten, Dokumentation und Backward-Compatibility-Garantien. Events werden bewusst designed, nicht zufällig entstehen gelassen.

Event-Kontrakt-Definition:

// Spring Boot - API-First Event-Design
/**
 * OrderPlaced Event - Public API Contract
 * 
 * Publiziert wenn eine Bestellung erfolgreich aufgegeben wurde.
 * 
 * Business Rules:
 * - Wird nur bei gültiger Zahlung publiziert
 * - Enthält alle Items zur Zeit der Bestellung
 * - totalAmount entspricht Summe aller Item-Preise inkl. Steuern
 * 
 * Consumer Expectations:
 * - PaymentService: Verarbeitet Zahlung basierend auf totalAmount
 * - InventoryService: Reserviert Items basierend auf items-Liste
 * - ShippingService: Bereitet Versand basierend auf customerId vor
 * 
 * Schema Stability: 
 * - Alle Felder sind required und dürfen nicht entfernt werden
 * - Neue optionale Felder können hinzugefügt werden
 * - Feldtypen dürfen nicht geändert werden
 */
@EventContract(
    name = "OrderPlaced",
    version = "1.0",
    description = "Published when a customer successfully places an order",
    compatibility = CompatibilityLevel.BACKWARD_COMPATIBLE
)
public class OrderPlaced {
    
    @EventField(
        description = "Unique identifier for this specific event instance",
        required = true,
        stability = FieldStability.STABLE
    )
    private String eventId;
    
    @EventField(
        description = "Business identifier of the placed order",
        required = true,
        stability = FieldStability.STABLE,
        businessKey = true
    )
    private String orderId;
    
    @EventField(
        description = "Customer who placed the order",
        required = true,
        stability = FieldStability.STABLE
    )
    private String customerId;
    
    @EventField(
        description = "Complete list of ordered items with quantities and prices",
        required = true,
        stability = FieldStability.STABLE
    )
    private List<OrderItem> items;
    
    @EventField(
        description = "Total order amount including taxes and fees",
        required = true,
        stability = FieldStability.STABLE,
        validation = "Must equal sum of all item totals"
    )
    private BigDecimal totalAmount;
    
    @EventField(
        description = "Timestamp when order was placed (ISO 8601)",
        required = true,
        stability = FieldStability.STABLE
    )
    private Instant placedAt;
    
    // Konstruktor, Getter, Builder...
}

// Event-Kontrakt Metadaten
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EventContract {
    String name();
    String version();
    String description();
    CompatibilityLevel compatibility() default CompatibilityLevel.BACKWARD_COMPATIBLE;
    String[] deprecatedIn() default {};
    String[] removedIn() default {};
}

public enum CompatibilityLevel {
    BACKWARD_COMPATIBLE,    // Neue Felder nur optional
    FORWARD_COMPATIBLE,     // Consumer ignorieren unbekannte Felder
    FULL_COMPATIBLE,        // Beide Richtungen
    BREAKING_CHANGE         // Expliziter Breaking Change
}
# Python - API-First Event-Kontrakte
from dataclasses import dataclass, field
from typing import Dict, Any, List, Optional
from enum import Enum
from datetime import datetime
import json

class FieldStability(Enum):
    STABLE = "stable"           # Feld wird nie geändert
    EVOLVING = "evolving"       # Feld kann kompatibel erweitert werden
    EXPERIMENTAL = "experimental"  # Feld kann sich ändern
    DEPRECATED = "deprecated"   # Feld wird entfernt

@dataclass
class EventFieldMeta:
    """Metadaten für Event-Felder"""
    description: str
    required: bool = True
    stability: FieldStability = FieldStability.STABLE
    business_key: bool = False
    validation_rules: Optional[str] = None

@dataclass
class EventContractMeta:
    """Metadaten für Event-Kontrakte"""
    name: str
    version: str
    description: str
    published_by: str
    consumers: List[str]
    stability_guarantees: Dict[str, str]
    breaking_changes: List[str] = field(default_factory=list)

class EventContractRegistry:
    """Registry für Event-Kontrakte und deren Metadaten"""
    
    def __init__(self):
        self.contracts = {}
        self.field_metadata = {}
    
    def register_contract(self, event_class: type, meta: EventContractMeta) -> None:
        """Registriert Event-Kontrakt mit Metadaten"""
        contract_key = f"{meta.name}.{meta.version}"
        self.contracts[contract_key] = {
            'event_class': event_class,
            'metadata': meta
        }
    
    def get_contract_documentation(self, event_name: str, version: str) -> Dict[str, Any]:
        """Generiert API-Dokumentation für Event-Kontrakt"""
        contract_key = f"{event_name}.{version}"
        
        if contract_key not in self.contracts:
            raise ValueError(f"Contract {contract_key} not found")
        
        contract = self.contracts[contract_key]
        event_class = contract['event_class']
        meta = contract['metadata']
        
        return {
            'name': meta.name,
            'version': meta.version,
            'description': meta.description,
            'published_by': meta.published_by,
            'consumers': meta.consumers,
            'fields': self._extract_field_docs(event_class),
            'stability_guarantees': meta.stability_guarantees,
            'breaking_changes': meta.breaking_changes,
            'example': self._generate_example(event_class)
        }
    
    def _extract_field_docs(self, event_class: type) -> Dict[str, Any]:
        """Extrahiert Feld-Dokumentation aus Event-Klasse"""
        # Vereinfachte Implementierung - in Realität würde man
        # Annotations und Typing-Informationen auswerten
        return {
            'event_id': {
                'type': 'string',
                'required': True,
                'description': 'Unique event identifier'
            },
            'order_id': {
                'type': 'string', 
                'required': True,
                'description': 'Business order identifier'
            }
            # Weitere Felder...
        }

# Konkrete Event-Kontrakte
@dataclass
class OrderPlacedContract:
    """API-First Design für OrderPlaced Event"""
    
    # Stabile Kern-Felder (niemals ändern)
    event_id: str
    order_id: str  
    customer_id: str
    total_amount: str  # String für Decimal-Kompatibilität
    placed_at: str     # ISO 8601 String
    
    # Evolving Fields (kompatible Erweiterung möglich)
    items: List[Dict[str, Any]]
    
    # Experimentelle Felder (können sich ändern)
    metadata: Optional[Dict[str, Any]] = None
    
    @classmethod
    def get_contract_metadata(cls) -> EventContractMeta:
        return EventContractMeta(
            name="OrderPlaced",
            version="1.0",
            description="Published when customer successfully places order",
            published_by="OrderService",
            consumers=["PaymentService", "InventoryService", "ShippingService"],
            stability_guarantees={
                "backward_compatibility": "All existing fields remain stable",
                "forward_compatibility": "New optional fields may be added",
                "field_types": "Field types never change",
                "required_fields": "Required fields never become optional"
            }
        )
    
    def validate_contract(self) -> List[str]:
        """Validiert Event gegen Kontrakt-Regeln"""
        violations = []
        
        # Business Rule Validations
        if not self.event_id:
            violations.append("event_id is required")
        
        if not self.order_id:
            violations.append("order_id is required")
            
        try:
            from decimal import Decimal
            amount = Decimal(self.total_amount)
            if amount <= 0:
                violations.append("total_amount must be positive")
        except:
            violations.append("total_amount must be valid decimal")
        
        return violations

Event-Dokumentation und Discovery:

// Spring Boot - Event-Registry und Dokumentation
@Component
public class EventContractRegistry {
    
    private final Map<String, EventContractInfo> registeredEvents = new HashMap<>();
    
    @PostConstruct
    public void registerKnownEvents() {
        // Automatische Registrierung via Classpath-Scanning
        registerEvent(OrderPlaced.class);
        registerEvent(PaymentProcessed.class);
        registerEvent(InventoryReserved.class);
    }
    
    public void registerEvent(Class<?> eventClass) {
        EventContract annotation = eventClass.getAnnotation(EventContract.class);
        if (annotation != null) {
            EventContractInfo info = EventContractInfo.builder()
                .name(annotation.name())
                .version(annotation.version())
                .description(annotation.description())
                .eventClass(eventClass)
                .fields(extractFieldInfo(eventClass))
                .build();
                
            registeredEvents.put(annotation.name(), info);
        }
    }
    
    @GetMapping("/api/events/contracts")
    public Map<String, Object> getEventCatalog() {
        return registeredEvents.entrySet().stream()
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> Map.of(
                    "contract", entry.getValue(),
                    "schema", generateJsonSchema(entry.getValue().getEventClass()),
                    "example", generateExample(entry.getValue().getEventClass())
                )
            ));
    }
    
    private Map<String, FieldInfo> extractFieldInfo(Class<?> eventClass) {
        Map<String, FieldInfo> fields = new HashMap<>();
        
        for (Field field : eventClass.getDeclaredFields()) {
            EventField annotation = field.getAnnotation(EventField.class);
            if (annotation != null) {
                fields.put(field.getName(), FieldInfo.builder()
                    .name(field.getName())
                    .type(field.getType().getSimpleName())
                    .description(annotation.description())
                    .required(annotation.required())
                    .stability(annotation.stability())
                    .build());
            }
        }
        
        return fields;
    }
}

19.3 Cross-Context Event-Design

Cross-Context Events überschreiten Bounded Context-Grenzen und erfordern besondere Designprinzipien, um lose Kopplung und Autonomie zu bewahren.

Integration Event Patterns:

// Spring Boot - Cross-Context Event-Design
public class CrossContextEventDesign {
    
    // Pattern 1: Domain Event → Integration Event Transformation
    public static class OrderDomainEvent {
        // Interne Domänen-Repräsentation (reich an Details)
        private String orderId;
        private CustomerId customerId;          // Value Object
        private List<OrderLine> orderLines;     // Domain-spezifische Struktur
        private Money totalAmount;              // Domain-spezifisches Value Object
        private OrderStatus status;             // Domain-Enum
        private Instant placedAt;
    }
    
    public static class OrderPlacedIntegrationEvent {
        // Vereinfachte Cross-Context-Repräsentation
        private String eventId;
        private String eventType = "OrderPlaced";
        private String sourceContext = "Order";
        private Instant occurredAt;
        
        // Nur für andere Kontexte relevante Daten
        private String orderId;
        private String customerId;              // String statt Value Object
        private BigDecimal totalAmount;         // Standard-Typ
        private String currency;
        private Map<String, String> orderItems; // Vereinfachte Item-Darstellung
        
        // Anti-Corruption Layer Informationen
        private String sourceVersion = "1.0";
        private Map<String, Object> metadata;
    }
    
    // Pattern 2: Kontext-spezifische Event-Übersetzung
    @Service
    public class OrderEventTranslationService {
        
        public OrderPlacedIntegrationEvent translateForIntegration(OrderDomainEvent domainEvent) {
            return OrderPlacedIntegrationEvent.builder()
                .eventId(UUID.randomUUID().toString())
                .orderId(domainEvent.getOrderId())
                .customerId(domainEvent.getCustomerId().getValue()) // Value Object → String
                .totalAmount(domainEvent.getTotalAmount().getAmount()) // Money → BigDecimal
                .currency(domainEvent.getTotalAmount().getCurrency().getCurrencyCode())
                .orderItems(transformOrderLines(domainEvent.getOrderLines()))
                .occurredAt(domainEvent.getPlacedAt())
                .metadata(createMetadata(domainEvent))
                .build();
        }
        
        private Map<String, String> transformOrderLines(List<OrderLine> orderLines) {
            // Vereinfachung für Cross-Context-Konsum
            return orderLines.stream()
                .collect(Collectors.toMap(
                    line -> line.getProductId().getValue(),
                    line -> String.valueOf(line.getQuantity())
                ));
        }
    }
    
    // Pattern 3: Kontext-Boundary Events
    public static class CustomerContextBoundaryEvents {
        
        // Event für Änderungen, die andere Kontexte interessieren
        public static class CustomerTierChanged {
            private String customerId;
            private String previousTier;
            private String newTier;
            private Instant changedAt;
            private String changeReason;
            
            // Explizit für Cross-Context-Konsum designed
            // Enthält nur Informationen, die andere Kontexte brauchen
        }
        
        // Event für interne Änderungen (bleibt im Kontext)
        public static class CustomerProfileUpdated {
            private String customerId;
            private CustomerProfile profile; // Internes Value Object
            private Instant updatedAt;
            
            // Nicht für Cross-Context-Konsum gedacht
        }
    }
}
# Python - Cross-Context Event-Translation und Anti-Corruption Layer
from dataclasses import dataclass, asdict
from typing import Dict, Any, Optional, List
from abc import ABC, abstractmethod
from datetime import datetime

class BoundedContextIdentifier(Enum):
    ORDER = "order"
    PAYMENT = "payment"
    INVENTORY = "inventory"
    SHIPPING = "shipping"
    CUSTOMER = "customer"

@dataclass
class CrossContextEventEnvelope:
    """Wrapper für Cross-Context Events"""
    # Metadaten für Cross-Context-Kommunikation
    event_id: str
    event_type: str
    source_context: BoundedContextIdentifier
    target_contexts: List[BoundedContextIdentifier]
    occurred_at: datetime
    correlation_id: Optional[str] = None
    causation_id: Optional[str] = None
    
    # Eigentliche Event-Daten
    payload: Dict[str, Any]
    
    # Anti-Corruption Layer Info
    schema_version: str = "1.0"
    translation_metadata: Dict[str, Any] = None

class EventTranslationService(ABC):
    """Basis für Event-Translation zwischen Kontexten"""
    
    @abstractmethod
    def translate_to_integration_event(self, domain_event: Any) -> CrossContextEventEnvelope:
        pass
    
    @abstractmethod
    def translate_from_integration_event(self, envelope: CrossContextEventEnvelope) -> Any:
        pass

class OrderEventTranslationService(EventTranslationService):
    """Übersetzt Order-Domain-Events für Cross-Context-Kommunikation"""
    
    def translate_to_integration_event(self, domain_event) -> CrossContextEventEnvelope:
        """Übersetzt reiches Domain-Event in vereinfachtes Integration-Event"""
        
        # Anti-Corruption: Domain-Komplexität vor anderen Kontexten verstecken
        simplified_payload = {
            'order_id': domain_event.order_id,
            'customer_id': str(domain_event.customer_id),  # Value Object → String
            'total_amount': str(domain_event.total_amount.amount),  # Money → String
            'currency': domain_event.total_amount.currency,
            'item_count': len(domain_event.order_lines),
            'categories': self._extract_categories(domain_event.order_lines)
        }
        
        return CrossContextEventEnvelope(
            event_id=self._generate_event_id(),
            event_type="OrderPlaced",
            source_context=BoundedContextIdentifier.ORDER,
            target_contexts=[
                BoundedContextIdentifier.PAYMENT,
                BoundedContextIdentifier.INVENTORY,
                BoundedContextIdentifier.SHIPPING
            ],
            occurred_at=domain_event.placed_at,
            payload=simplified_payload,
            translation_metadata={
                'original_event_type': type(domain_event).__name__,
                'translation_time': datetime.now(),
                'simplified_fields': ['customer_id', 'total_amount', 'order_lines']
            }
        )
    
    def translate_from_integration_event(self, envelope: CrossContextEventEnvelope):
        """Übersetzt Integration-Event zurück in Domain-Kontext"""
        # In der Regel nicht notwendig, da Integration-Events
        # von anderen Kontexten kommen
        pass
    
    def _extract_categories(self, order_lines: List) -> List[str]:
        """Extrahiert Produkt-Kategorien für andere Kontexte"""
        # Vereinfachung für Cross-Context-Konsum
        return list(set(line.product.category for line in order_lines))

class ContextBoundaryEventDesign:
    """Design-Patterns für Context-Boundary Events"""
    
    @dataclass
    class CustomerEligibilityChanged:
        """Optimal für Cross-Context: Enthält Entscheidungskontext"""
        customer_id: str
        previous_tier: str
        new_tier: str
        tier_benefits: List[str]
        change_effective_date: datetime
        change_reason: str
        
        # Ermöglicht autonome Entscheidungen in anderen Kontexten
        def enables_free_shipping(self) -> bool:
            return self.new_tier in ['premium', 'vip']
        
        def enables_priority_support(self) -> bool:
            return self.new_tier == 'vip'
    
    @dataclass
    class ProductAvailabilityChanged:
        """Cross-Context Event mit vollständigem Kontext"""
        product_id: str
        previous_stock_level: int
        new_stock_level: int
        availability_status: str  # "available", "low_stock", "out_of_stock"
        restock_expected_date: Optional[datetime]
        affected_categories: List[str]
        
        # Ermöglicht autonome Reaktionen
        def should_notify_customers(self) -> bool:
            return (self.previous_stock_level == 0 and 
                   self.new_stock_level > 0)
        
        def should_trigger_reorder(self) -> bool:
            return (self.new_stock_level < 10 and 
                   self.availability_status == "low_stock")

class CrossContextEventValidator:
    """Validiert Events für Cross-Context-Tauglichkeit"""
    
    def validate_cross_context_event(self, event_data: Dict[str, Any]) -> List[str]:
        """Prüft Event auf Cross-Context-Design-Prinzipien"""
        issues = []
        
        # Prüfe auf Domain-spezifische Value Objects
        if self._contains_value_objects(event_data):
            issues.append("Contains domain-specific value objects - use primitives for cross-context")
        
        # Prüfe auf zu viele interne Details
        if len(event_data) > 10:
            issues.append("Too many fields - consider simplifying for cross-context consumption")
        
        # Prüfe auf fehlenden Entscheidungskontext
        if not self._has_decision_context(event_data):
            issues.append("Missing decision context - other contexts cannot act autonomously")
        
        return issues
    
    def _contains_value_objects(self, data: Dict[str, Any]) -> bool:
        """Prüft auf komplexe Domain-Objekte"""
        # Vereinfachte Implementierung
        return any(isinstance(value, dict) and 'class' in str(type(value)) 
                  for value in data.values())
    
    def _has_decision_context(self, data: Dict[str, Any]) -> bool:
        """Prüft auf ausreichenden Kontext für autonome Entscheidungen"""
        # Heuristic: Events sollten mindestens 3-4 relevante Felder haben
        relevant_fields = [k for k, v in data.items() 
                          if not k.startswith('_') and v is not None]
        return len(relevant_fields) >= 3

Context Mapping für Events:

Kontext Published Events Consumed Events Integration Points
Order OrderPlaced, OrderCancelled PaymentProcessed, InventoryReserved Payment, Inventory, Shipping
Payment PaymentProcessed, PaymentDeclined OrderPlaced Order, Fraud
Inventory InventoryReserved, StockLevelChanged OrderPlaced, OrderCancelled Order, Procurement
Shipping ShippingPrepared, OrderShipped OrderPlaced, PaymentProcessed Order, Payment, Logistics

Event-Namen, Kontrakte und Context-Design bilden das Fundament verständlicher und maintainbarer Event-Architekturen. Konsistente Naming-Conventions schaffen Klarheit, API-First Design gewährleistet stabile Kontrakte, und durchdachtes Cross-Context-Design ermöglicht autonome System-Evolution bei loser Kopplung.