dotnet-cqrs/Svrnty.CQRS.Events/Sagas/SagaServiceCollectionExtensions.cs

103 lines
3.2 KiB
C#

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Svrnty.CQRS.Events.Abstractions.Sagas;
namespace Svrnty.CQRS.Events.Sagas;
/// <summary>
/// Service collection extensions for saga orchestration.
/// </summary>
public static class SagaServiceCollectionExtensions
{
/// <summary>
/// Add saga orchestration infrastructure.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="useInMemoryStateStore">Use in-memory state store (default: true).</param>
/// <returns>The service collection for chaining.</returns>
public static IServiceCollection AddSagaOrchestration(
this IServiceCollection services,
bool useInMemoryStateStore = true)
{
if (services == null)
throw new ArgumentNullException(nameof(services));
// Register core services
services.TryAddSingleton<ISagaRegistry, SagaRegistry>();
services.TryAddSingleton<ISagaOrchestrator, SagaOrchestrator>();
// Register state store
if (useInMemoryStateStore)
{
services.TryAddSingleton<ISagaStateStore, InMemorySagaStateStore>();
}
return services;
}
/// <summary>
/// Register a saga definition.
/// </summary>
/// <typeparam name="TSaga">The saga type.</typeparam>
/// <param name="services">The service collection.</param>
/// <param name="sagaName">The saga name.</param>
/// <param name="configure">Action to configure saga definition.</param>
/// <returns>The service collection for chaining.</returns>
public static IServiceCollection AddSaga<TSaga>(
this IServiceCollection services,
string sagaName,
Action<SagaDefinition> configure)
where TSaga : class, ISaga
{
if (services == null)
throw new ArgumentNullException(nameof(services));
if (string.IsNullOrWhiteSpace(sagaName))
throw new ArgumentException("Saga name cannot be null or empty", nameof(sagaName));
if (configure == null)
throw new ArgumentNullException(nameof(configure));
// Register saga type
services.TryAddTransient<TSaga>();
// Create and configure definition
var definition = new SagaDefinition(sagaName);
configure(definition);
// Register definition with registry
services.AddSingleton<ISagaRegistration>(sp =>
{
var registry = sp.GetRequiredService<ISagaRegistry>();
registry.Register<TSaga>(definition);
return new SagaRegistration<TSaga>(sagaName);
});
return services;
}
}
/// <summary>
/// Marker interface for saga registration tracking.
/// </summary>
internal interface ISagaRegistration
{
string SagaName { get; }
Type SagaType { get; }
}
/// <summary>
/// Saga registration marker.
/// </summary>
internal sealed class SagaRegistration<TSaga> : ISagaRegistration
where TSaga : ISaga
{
public SagaRegistration(string sagaName)
{
SagaName = sagaName ?? throw new ArgumentNullException(nameof(sagaName));
SagaType = typeof(TSaga);
}
public string SagaName { get; }
public Type SagaType { get; }
}