146 lines
5.3 KiB
C#
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;
|
|
}
|
|
}
|