using System; using Svrnty.CQRS.Events.Abstractions.Models; using System.Collections.Generic; using System.Linq; using Svrnty.CQRS.Events.Abstractions.EventStore; namespace Svrnty.CQRS.Events.Abstractions.Models; /// /// Base class for workflows that emit correlated events. /// A workflow represents a logical business process that may span multiple commands. /// Each workflow instance has a unique ID that serves as the correlation ID for all events emitted within it. /// /// /// /// Design Philosophy: /// - Workflows are the primary abstraction for event emission (events are implementation details) /// - Each workflow instance represents a single logical process (e.g., one invitation, one order) /// - Workflow ID becomes the correlation ID for all events /// /// /// Developer Usage: /// Create a workflow class by inheriting from this base class: /// /// public class InvitationWorkflow : Workflow /// { /// public void EmitInvited(UserInvitedEvent e) => Emit(e); /// public void EmitAccepted(UserInviteAcceptedEvent e) => Emit(e); /// } /// /// /// /// Framework Usage: /// The framework manages workflow lifecycle: /// - Sets when workflow starts or continues /// - Sets based on whether this is a new workflow or continuation /// - Reads after command execution /// - Calls to set correlation IDs on all events /// /// public abstract class Workflow { /// /// Unique identifier for this workflow instance. /// Set by the framework when the workflow is started or continued. /// This ID becomes the correlation ID for all events emitted by this workflow. /// /// /// Framework Use: This property is set by the framework and should not be modified by user code. /// public string Id { get; set; } = string.Empty; /// /// Indicates whether this is a new workflow instance (true) or a continuation of an existing workflow (false). /// Set by the framework based on whether the workflow was started or continued. /// /// /// This can be useful for workflow logic that should only run once (e.g., validation on start). /// Framework Use: This property is set by the framework and should not be modified by user code. /// public bool IsNew { get; set; } /// /// Internal collection of events that have been emitted but not yet persisted. /// The framework reads this after command execution to emit events. /// private readonly List _pendingEvents = new(); /// /// Gets the pending events that have been emitted within this workflow. /// Used by the framework to retrieve events after command execution. /// /// /// Framework Use Only: This property is for framework use and should not be accessed by user code. /// public IReadOnlyList PendingEvents => _pendingEvents.AsReadOnly(); /// /// Emits an event within this workflow. /// The event will be assigned this workflow's ID as its correlation ID by the framework. /// /// The type of event to emit. Must implement . /// The event to emit. /// Thrown if is null. /// /// /// This method is protected so only derived workflow classes can emit events. /// Events are collected and will be persisted by the framework after the command handler completes. /// /// /// Usage Example: /// /// protected void EmitInvited(UserInvitedEvent e) => Emit(e); /// /// /// protected void Emit(TEvent @event) where TEvent : ICorrelatedEvent { if (@event == null) throw new ArgumentNullException(nameof(@event)); _pendingEvents.Add(@event); } /// /// Assigns this workflow's ID as the correlation ID to all pending events. /// Called by the framework before events are persisted. /// /// Thrown if workflow ID is not set. /// /// Framework Use Only: This method is for framework use and should not be called by user code. /// public void AssignCorrelationIds() { if (string.IsNullOrWhiteSpace(Id)) throw new InvalidOperationException("Workflow ID must be set before assigning correlation IDs."); foreach (var @event in _pendingEvents) { @event.CorrelationId = Id; } } /// /// Clears all pending events. /// Called by the framework after events have been persisted. /// /// /// Framework Use Only: This method is for framework use and should not be called by user code. /// public void ClearPendingEvents() { _pendingEvents.Clear(); } /// /// Gets the number of events that have been emitted within this workflow. /// Useful for testing and diagnostics. /// /// /// Framework Use Only: This property is for framework use and diagnostics. /// public int PendingEventCount => _pendingEvents.Count; }