using System; using Svrnty.CQRS.Events.Abstractions.Models; using Svrnty.CQRS.Events.Abstractions.Schema; using Svrnty.CQRS.Events.Abstractions.EventStore; using Svrnty.CQRS.Events.Abstractions; namespace Svrnty.Sample.Events; // ============================================================================ // PHASE 5: EVENT VERSIONING DEMONSTRATION // ============================================================================ // This file demonstrates event schema evolution with automatic upcasting. // Shows how to evolve event schemas over time without breaking compatibility. // ============================================================================ /// /// Version 1 of UserCreatedEvent (initial schema). /// Original design with a single "FullName" field. /// /// /// /// Version 1 Schema: /// - UserId: int /// - FullName: string (combined first and last name) /// /// [EventVersion(1)] public sealed record UserCreatedEventV1 : CorrelatedEvent { public required int UserId { get; init; } public required string FullName { get; init; } } /// /// Version 2 of UserCreatedEvent (evolved schema). /// Improved design that separates name components and adds email. /// /// /// /// Version 2 Schema: /// - UserId: int /// - FirstName: string (split from FullName) /// - LastName: string (split from FullName) /// - Email: string (new field) /// /// /// Schema Changes from V1: /// - FullName → FirstName + LastName (split transformation) /// - Added Email field (default: "unknown@example.com") /// /// /// Automatic Upcasting: /// When a V1 event is consumed by a subscription configured for V2, /// the framework automatically calls to transform it. /// /// [EventVersion(2, UpcastFrom = typeof(UserCreatedEventV1))] public sealed record UserCreatedEventV2 : CorrelatedEvent { public required int UserId { get; init; } public required string FirstName { get; init; } public required string LastName { get; init; } public required string Email { get; init; } /// /// Convention-based upcaster: Transforms V1 events to V2. /// /// /// /// The framework discovers this method automatically via reflection. /// Method signature must match: public static {ToType} UpcastFrom({FromType}) /// /// /// Transformation Logic: /// - Split FullName on first space /// - First part becomes FirstName /// - Remaining parts become LastName /// - Email defaults to "unknown@example.com" (data not available in V1) /// - Preserve correlation metadata (EventId, CorrelationId, OccurredAt) /// /// public static UserCreatedEventV2 UpcastFrom(UserCreatedEventV1 v1) { // Split full name into components var parts = v1.FullName.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries); var firstName = parts.Length > 0 ? parts[0] : "Unknown"; var lastName = parts.Length > 1 ? parts[1] : ""; return new UserCreatedEventV2 { // Preserve correlation metadata from V1 EventId = v1.EventId, CorrelationId = v1.CorrelationId, OccurredAt = v1.OccurredAt, // Transform data fields UserId = v1.UserId, FirstName = firstName, LastName = lastName, Email = "unknown@example.com" // Default for new field }; } } /// /// Version 3 of UserCreatedEvent (further evolution). /// Adds phone number and makes email optional. /// /// /// /// Version 3 Schema: /// - UserId: int /// - FirstName: string /// - LastName: string /// - Email: string (now nullable) /// - PhoneNumber: string? (new optional field) /// /// /// Multi-Hop Upcasting: /// The framework can automatically upcast V1 → V2 → V3 by chaining upcasters. /// You only need to define V1→V2 and V2→V3; the framework handles V1→V3. /// /// [EventVersion(3, UpcastFrom = typeof(UserCreatedEventV2))] public sealed record UserCreatedEventV3 : CorrelatedEvent { public required int UserId { get; init; } public required string FirstName { get; init; } public required string LastName { get; init; } public string? Email { get; init; } public string? PhoneNumber { get; init; } /// /// Upcaster: Transforms V2 events to V3. /// public static UserCreatedEventV3 UpcastFrom(UserCreatedEventV2 v2) { return new UserCreatedEventV3 { // Preserve correlation metadata EventId = v2.EventId, CorrelationId = v2.CorrelationId, OccurredAt = v2.OccurredAt, // Copy existing fields UserId = v2.UserId, FirstName = v2.FirstName, LastName = v2.LastName, Email = v2.Email != "unknown@example.com" ? v2.Email : null, PhoneNumber = null // New field, no data available from V2 }; } }