dotnet-cqrs/Svrnty.CQRS.Events.Abstractions/EventHandlers/ICommandHandlerWithWorkflow.cs

132 lines
5.3 KiB
C#

using System.Threading;
using Svrnty.CQRS.Events.Abstractions.Models;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Events.Abstractions.EventHandlers;
/// <summary>
/// Handler interface for commands that participate in a workflow and return a result.
/// The workflow manages event emission and correlation.
/// </summary>
/// <typeparam name="TCommand">The command type to handle.</typeparam>
/// <typeparam name="TResult">The result type returned by the handler.</typeparam>
/// <typeparam name="TWorkflow">The workflow type that manages events for this command. Must inherit from <see cref="Workflow"/>.</typeparam>
/// <remarks>
/// <para>
/// <strong>Workflow Pattern:</strong>
/// Instead of manually managing event contexts and correlation IDs, handlers receive a workflow instance.
/// The workflow encapsulates the business process and provides methods to emit events.
/// All events emitted within the workflow are automatically correlated using the workflow's ID.
/// </para>
/// <para>
/// <strong>Example Usage:</strong>
/// <code>
/// public class InviteUserCommandHandler
/// : ICommandHandlerWithWorkflow&lt;InviteUserCommand, string, InvitationWorkflow&gt;
/// {
/// public async Task&lt;string&gt; HandleAsync(
/// InviteUserCommand command,
/// InvitationWorkflow workflow,
/// CancellationToken cancellationToken)
/// {
/// // Business logic
/// var invitationId = Guid.NewGuid().ToString();
///
/// // Emit event via workflow (automatically correlated)
/// workflow.Emit(new UserInvitedEvent
/// {
/// InvitationId = invitationId,
/// Email = command.Email
/// });
///
/// // Return workflow ID for follow-up commands
/// return workflow.Id;
/// }
/// }
/// </code>
/// </para>
/// <para>
/// <strong>Framework Behavior:</strong>
/// - The framework creates/loads the workflow instance before calling the handler
/// - Workflow.Id is set (either new GUID or existing workflow ID)
/// - Workflow.IsNew indicates if this is a new workflow or continuation
/// - After the handler completes, the framework emits all events collected in the workflow
/// - All events receive the workflow ID as their correlation ID
/// </para>
/// </remarks>
public interface ICommandHandlerWithWorkflow<in TCommand, TResult, TWorkflow>
where TCommand : class
where TWorkflow : Workflow
{
/// <summary>
/// Handles the command within the context of a workflow.
/// </summary>
/// <param name="command">The command to handle.</param>
/// <param name="workflow">The workflow instance managing events for this command execution.</param>
/// <param name="cancellationToken">Cancellation token for the async operation.</param>
/// <returns>The result of handling the command.</returns>
/// <remarks>
/// Emit events by calling methods on the workflow instance (which internally call workflow.Emit()).
/// The framework will persist all emitted events after this method completes successfully.
/// </remarks>
Task<TResult> HandleAsync(
TCommand command,
TWorkflow workflow,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Handler interface for commands that participate in a workflow but do not return a result.
/// The workflow manages event emission and correlation.
/// </summary>
/// <typeparam name="TCommand">The command type to handle.</typeparam>
/// <typeparam name="TWorkflow">The workflow type that manages events for this command. Must inherit from <see cref="Workflow"/>.</typeparam>
/// <remarks>
/// <para>
/// This is the "no result" variant of <see cref="ICommandHandlerWithWorkflow{TCommand, TResult, TWorkflow}"/>.
/// Use this when your command performs an action but doesn't need to return a value.
/// </para>
/// <para>
/// <strong>Example Usage:</strong>
/// <code>
/// public class DeclineInviteCommandHandler
/// : ICommandHandlerWithWorkflow&lt;DeclineInviteCommand, InvitationWorkflow&gt;
/// {
/// public async Task HandleAsync(
/// DeclineInviteCommand command,
/// InvitationWorkflow workflow,
/// CancellationToken cancellationToken)
/// {
/// workflow.Emit(new UserInviteDeclinedEvent
/// {
/// InvitationId = command.InvitationId,
/// Reason = command.Reason
/// });
///
/// await Task.CompletedTask;
/// }
/// }
/// </code>
/// </para>
/// </remarks>
public interface ICommandHandlerWithWorkflow<in TCommand, TWorkflow>
where TCommand : class
where TWorkflow : Workflow
{
/// <summary>
/// Handles the command within the context of a workflow.
/// </summary>
/// <param name="command">The command to handle.</param>
/// <param name="workflow">The workflow instance managing events for this command execution.</param>
/// <param name="cancellationToken">Cancellation token for the async operation.</param>
/// <returns>A task representing the async operation.</returns>
/// <remarks>
/// Emit events by calling methods on the workflow instance (which internally call workflow.Emit()).
/// The framework will persist all emitted events after this method completes successfully.
/// </remarks>
Task HandleAsync(
TCommand command,
TWorkflow workflow,
CancellationToken cancellationToken = default);
}