103 lines
3.2 KiB
C#
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; }
|
|
}
|