96 lines
4.1 KiB
C#
96 lines
4.1 KiB
C#
using System;
|
|
using Svrnty.CQRS.Events.Abstractions.Notifications;
|
|
using Svrnty.CQRS.Events.Abstractions.Delivery;
|
|
using Svrnty.CQRS.Events.Delivery;
|
|
using Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Svrnty.CQRS.Events.Abstractions;
|
|
using Svrnty.CQRS.Events.Abstractions.Subscriptions;
|
|
|
|
namespace Svrnty.CQRS.Events.Subscriptions;
|
|
|
|
/// <summary>
|
|
/// Decorator that integrates Phase 8 persistent subscription delivery with the existing event delivery pipeline.
|
|
/// This wraps the default IEventDeliveryService and adds SignalR/gRPC-based delivery to active persistent subscriptions.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <strong>Integration Point:</strong>
|
|
/// This decorator is registered in the DI container when Phase 8 is enabled, wrapping the default EventDeliveryService.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Responsibilities:</strong>
|
|
/// - Delegates to the wrapped IEventDeliveryService for standard subscription management
|
|
/// - Delivers events to active persistent subscriptions via IPersistentSubscriptionDeliveryService
|
|
/// - Pushes events to connected SignalR and gRPC clients in real-time
|
|
/// - Tracks sequence numbers for catch-up on reconnect
|
|
/// </para>
|
|
/// </remarks>
|
|
public sealed class PersistentSubscriptionDeliveryDecorator : IEventDeliveryService
|
|
{
|
|
private readonly IEventDeliveryService _inner;
|
|
private readonly IPersistentSubscriptionDeliveryService? _persistentDeliveryService;
|
|
private readonly IPersistentSubscriptionStore? _subscriptionStore;
|
|
private readonly ILogger<PersistentSubscriptionDeliveryDecorator> _logger;
|
|
|
|
public PersistentSubscriptionDeliveryDecorator(
|
|
IEventDeliveryService inner,
|
|
IPersistentSubscriptionDeliveryService? persistentDeliveryService,
|
|
IPersistentSubscriptionStore? subscriptionStore,
|
|
ILogger<PersistentSubscriptionDeliveryDecorator> logger)
|
|
{
|
|
_inner = inner ?? throw new ArgumentNullException(nameof(inner));
|
|
_persistentDeliveryService = persistentDeliveryService;
|
|
_subscriptionStore = subscriptionStore;
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
public async Task DeliverEventAsync(ICorrelatedEvent @event, long sequence, CancellationToken cancellationToken = default)
|
|
{
|
|
// First, delegate to the wrapped service for standard processing
|
|
await _inner.DeliverEventAsync(@event, sequence, cancellationToken);
|
|
|
|
// Then, deliver to Phase 8 persistent subscriptions if enabled
|
|
if (_persistentDeliveryService != null)
|
|
{
|
|
try
|
|
{
|
|
var deliveredCount = await _persistentDeliveryService.DeliverEventAsync(
|
|
@event.CorrelationId,
|
|
@event,
|
|
sequence,
|
|
cancellationToken);
|
|
|
|
if (deliveredCount > 0)
|
|
{
|
|
_logger.LogDebug(
|
|
"Delivered event {EventType} (sequence {Sequence}) to {Count} persistent subscription(s)",
|
|
@event.GetType().Name,
|
|
sequence,
|
|
deliveredCount);
|
|
}
|
|
|
|
// Push events to connected gRPC/SignalR clients via event notifiers
|
|
// This happens after state updates so clients receive events immediately
|
|
if (_subscriptionStore != null)
|
|
{
|
|
// Note: Event notifiers (gRPC, SignalR) are called by EventEmitter via IEventNotifier
|
|
// This is handled separately in the event emission pipeline
|
|
// We could optionally call notifiers here as well for immediate push
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log but don't fail the entire delivery if Phase 8 delivery fails
|
|
_logger.LogError(
|
|
ex,
|
|
"Error delivering event {EventType} (sequence {Sequence}) to persistent subscriptions",
|
|
@event.GetType().Name,
|
|
sequence);
|
|
}
|
|
}
|
|
}
|
|
}
|