169 lines
4.0 KiB
C#
169 lines
4.0 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Svrnty.CQRS.Events.Abstractions.Sagas;
|
|
|
|
/// <summary>
|
|
/// Represents a long-running business process (saga) that coordinates multiple steps.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Sagas implement distributed transactions using compensation rather than two-phase commit.
|
|
/// Each step has a corresponding compensation action that undoes its effects.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Saga Pattern:</strong>
|
|
/// - Execute steps sequentially
|
|
/// - If a step fails, execute compensations in reverse order
|
|
/// - Supports timeouts, retries, and state persistence
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface ISaga
|
|
{
|
|
/// <summary>
|
|
/// Unique identifier for this saga instance.
|
|
/// </summary>
|
|
string SagaId { get; }
|
|
|
|
/// <summary>
|
|
/// Correlation ID linking this saga to related events/commands.
|
|
/// </summary>
|
|
string CorrelationId { get; }
|
|
|
|
/// <summary>
|
|
/// Name of the saga type (for tracking and monitoring).
|
|
/// </summary>
|
|
string SagaName { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a single step in a saga.
|
|
/// </summary>
|
|
public interface ISagaStep
|
|
{
|
|
/// <summary>
|
|
/// Name of this step.
|
|
/// </summary>
|
|
string StepName { get; }
|
|
|
|
/// <summary>
|
|
/// Execute the step's action.
|
|
/// </summary>
|
|
/// <param name="context">The saga execution context.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Task representing the async operation.</returns>
|
|
Task ExecuteAsync(ISagaContext context, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Compensate (undo) the step's action.
|
|
/// </summary>
|
|
/// <param name="context">The saga execution context.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Task representing the async operation.</returns>
|
|
Task CompensateAsync(ISagaContext context, CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context available to saga steps during execution.
|
|
/// </summary>
|
|
public interface ISagaContext
|
|
{
|
|
/// <summary>
|
|
/// The saga instance.
|
|
/// </summary>
|
|
ISaga Saga { get; }
|
|
|
|
/// <summary>
|
|
/// Current state of the saga.
|
|
/// </summary>
|
|
SagaState State { get; }
|
|
|
|
/// <summary>
|
|
/// Saga data (shared state across steps).
|
|
/// </summary>
|
|
ISagaData Data { get; }
|
|
|
|
/// <summary>
|
|
/// Get a value from saga data.
|
|
/// </summary>
|
|
T? Get<T>(string key);
|
|
|
|
/// <summary>
|
|
/// Set a value in saga data.
|
|
/// </summary>
|
|
void Set<T>(string key, T value);
|
|
|
|
/// <summary>
|
|
/// Check if a key exists in saga data.
|
|
/// </summary>
|
|
bool Contains(string key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saga data storage (key-value pairs).
|
|
/// </summary>
|
|
public interface ISagaData
|
|
{
|
|
/// <summary>
|
|
/// Get a value.
|
|
/// </summary>
|
|
T? Get<T>(string key);
|
|
|
|
/// <summary>
|
|
/// Set a value.
|
|
/// </summary>
|
|
void Set<T>(string key, T value);
|
|
|
|
/// <summary>
|
|
/// Check if a key exists.
|
|
/// </summary>
|
|
bool Contains(string key);
|
|
|
|
/// <summary>
|
|
/// Get all data as dictionary.
|
|
/// </summary>
|
|
System.Collections.Generic.IDictionary<string, object> GetAll();
|
|
}
|
|
|
|
/// <summary>
|
|
/// State of a saga instance.
|
|
/// </summary>
|
|
public enum SagaState
|
|
{
|
|
/// <summary>
|
|
/// Saga has not started yet.
|
|
/// </summary>
|
|
NotStarted = 0,
|
|
|
|
/// <summary>
|
|
/// Saga is currently executing steps.
|
|
/// </summary>
|
|
Running = 1,
|
|
|
|
/// <summary>
|
|
/// Saga completed successfully.
|
|
/// </summary>
|
|
Completed = 2,
|
|
|
|
/// <summary>
|
|
/// Saga is compensating (rolling back).
|
|
/// </summary>
|
|
Compensating = 3,
|
|
|
|
/// <summary>
|
|
/// Saga was compensated (rolled back).
|
|
/// </summary>
|
|
Compensated = 4,
|
|
|
|
/// <summary>
|
|
/// Saga failed and could not be compensated.
|
|
/// </summary>
|
|
Failed = 5,
|
|
|
|
/// <summary>
|
|
/// Saga is paused waiting for external event.
|
|
/// </summary>
|
|
Paused = 6
|
|
}
|