dotnet-cqrs/Svrnty.Sample/BackgroundServices/EventConsumerBackgroundService.cs

146 lines
5.3 KiB
C#

using Microsoft.Extensions.Hosting;
using Svrnty.Sample.Events;
using Svrnty.CQRS.Events.Abstractions.Subscriptions;
using Svrnty.CQRS.Events.Abstractions.EventStore;
using Microsoft.Extensions.Logging;
using Svrnty.CQRS.Events.Abstractions;
using System;
using System.Threading;
using System.Threading.Tasks;
using Svrnty.Sample.Workflows;
namespace Svrnty.Sample.BackgroundServices;
/// <summary>
/// Background service that demonstrates consuming events from event streams.
/// </summary>
/// <remarks>
/// <para>
/// <strong>Phase 1.4 Event Consumption:</strong>
/// This service shows how to consume events using <see cref="IEventSubscriptionClient"/>.
/// It subscribes to the "user-analytics" subscription (broadcast mode) and logs all events.
/// </para>
/// <para>
/// <strong>Usage Patterns:</strong>
/// - Broadcast mode: All consumer instances receive all events (great for logging/analytics)
/// - Exclusive mode: Only one consumer receives each event (great for work distribution)
/// </para>
/// <para>
/// <strong>Example:</strong>
/// This consumer demonstrates the broadcast pattern where multiple services can
/// independently observe and react to events without affecting each other.
/// </para>
/// </remarks>
public class EventConsumerBackgroundService : BackgroundService
{
private readonly IEventSubscriptionClient _subscriptionClient;
private readonly ILogger<EventConsumerBackgroundService> _logger;
public EventConsumerBackgroundService(
IEventSubscriptionClient subscriptionClient,
ILogger<EventConsumerBackgroundService> logger)
{
_subscriptionClient = subscriptionClient;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Event consumer starting...");
// Wait a bit for the application to fully start
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
_logger.LogInformation("Subscribing to 'user-analytics' subscription (broadcast mode)...");
var consumerId = $"analytics-{Guid.NewGuid():N}";
try
{
// Subscribe to the user-analytics subscription
// This is a broadcast subscription, so multiple consumers can receive the same events
await foreach (var @event in _subscriptionClient.SubscribeAsync(
"user-analytics",
consumerId,
stoppingToken))
{
// Process the event
await ProcessEventAsync(@event, stoppingToken);
}
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Event consumer stopping gracefully...");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in event consumer");
}
}
private Task ProcessEventAsync(ICorrelatedEvent @event, CancellationToken cancellationToken)
{
// Log the event details
_logger.LogInformation(
"[ANALYTICS] Received event: {EventType} (EventId: {EventId}, CorrelationId: {CorrelationId}, OccurredAt: {OccurredAt})",
@event.GetType().Name,
@event.EventId,
@event.CorrelationId,
@event.OccurredAt);
// Type-specific processing
switch (@event)
{
case UserAddedEvent userAdded:
_logger.LogInformation(
"[ANALYTICS] User added: UserId={UserId}, Name={Name}",
userAdded.UserId,
userAdded.Name);
break;
case UserRemovedEvent userRemoved:
_logger.LogInformation(
"[ANALYTICS] User removed: UserId={UserId}",
userRemoved.UserId);
break;
case UserInvitedEvent userInvited:
_logger.LogInformation(
"[ANALYTICS] User invited: InvitationId={InvitationId}, Email={Email}, Inviter={Inviter}",
userInvited.InvitationId,
userInvited.Email,
userInvited.InviterName);
break;
case UserInviteAcceptedEvent inviteAccepted:
_logger.LogInformation(
"[ANALYTICS] Invitation accepted: InvitationId={InvitationId}, UserId={UserId}, Name={Name}",
inviteAccepted.InvitationId,
inviteAccepted.UserId,
inviteAccepted.Name);
break;
case UserInviteDeclinedEvent inviteDeclined:
_logger.LogInformation(
"[ANALYTICS] Invitation declined: InvitationId={InvitationId}, Reason={Reason}",
inviteDeclined.InvitationId,
inviteDeclined.Reason ?? "(no reason provided)");
break;
default:
_logger.LogInformation(
"[ANALYTICS] Unknown event type: {EventType}",
@event.GetType().Name);
break;
}
// In a real application, you might:
// - Send metrics to a monitoring system
// - Update analytics dashboards
// - Trigger notifications
// - Store audit logs
return Task.CompletedTask;
}
}