165 lines
6.3 KiB
C#
165 lines
6.3 KiB
C#
using Microsoft.Extensions.Hosting;
|
|
using Svrnty.Sample.Events;
|
|
using Svrnty.CQRS.Events.Abstractions.Delivery;
|
|
using Svrnty.CQRS.Events.Abstractions.Models;
|
|
using Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
using Microsoft.Extensions.Logging;
|
|
using Svrnty.CQRS.Events.Abstractions;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Svrnty.Sample.Workflows;
|
|
|
|
namespace Svrnty.Sample.BackgroundServices;
|
|
|
|
/// <summary>
|
|
/// Background service that demonstrates consuming events from RabbitMQ (cross-service communication).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <strong>Phase 4: Cross-Service Event Streaming:</strong>
|
|
/// This service shows how to consume events from RabbitMQ using <see cref="IExternalEventDeliveryProvider"/>.
|
|
/// It subscribes to external event streams published by other services.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Difference from EventConsumerBackgroundService:</strong>
|
|
/// - EventConsumerBackgroundService: Consumes events from internal event store (same process/service)
|
|
/// - RabbitMQEventConsumerBackgroundService: Consumes events from RabbitMQ (cross-service)
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Usage Pattern:</strong>
|
|
/// External event delivery allows multiple independent services to communicate via events.
|
|
/// This enables microservices architecture where services can react to events from other services.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class RabbitMQEventConsumerBackgroundService : BackgroundService
|
|
{
|
|
private readonly IExternalEventDeliveryProvider _rabbitMq;
|
|
private readonly ILogger<RabbitMQEventConsumerBackgroundService> _logger;
|
|
|
|
public RabbitMQEventConsumerBackgroundService(
|
|
IExternalEventDeliveryProvider rabbitMq,
|
|
ILogger<RabbitMQEventConsumerBackgroundService> logger)
|
|
{
|
|
_rabbitMq = rabbitMq;
|
|
_logger = logger;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("RabbitMQ event consumer starting...");
|
|
|
|
// Wait a bit for the application to fully start
|
|
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
|
|
|
|
_logger.LogInformation("Subscribing to 'UserWorkflow' stream from RabbitMQ...");
|
|
|
|
var consumerId = $"rabbitmq-consumer-{Guid.NewGuid():N}";
|
|
|
|
try
|
|
{
|
|
// Subscribe to external events from RabbitMQ
|
|
// This demonstrates cross-service communication where events published
|
|
// by one service (e.g., UserService) can be consumed by another (e.g., EmailService)
|
|
// Stream name matches the workflow type name (UserWorkflow)
|
|
await _rabbitMq.SubscribeExternalAsync(
|
|
streamName: "UserWorkflow",
|
|
subscriptionId: "email-service",
|
|
consumerId: consumerId,
|
|
eventHandler: ProcessEventAsync,
|
|
cancellationToken: stoppingToken);
|
|
}
|
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
|
{
|
|
_logger.LogInformation("RabbitMQ event consumer stopping gracefully...");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in RabbitMQ event consumer");
|
|
}
|
|
}
|
|
|
|
private Task ProcessEventAsync(
|
|
ICorrelatedEvent @event,
|
|
IDictionary<string, string> metadata,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
// Log the event details
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Received external event: {EventType} (EventId: {EventId}, CorrelationId: {CorrelationId})",
|
|
@event.GetType().Name,
|
|
@event.EventId,
|
|
@event.CorrelationId);
|
|
|
|
// Log metadata
|
|
foreach (var (key, value) in metadata)
|
|
{
|
|
_logger.LogDebug("[RABBITMQ] Metadata: {Key} = {Value}", key, value);
|
|
}
|
|
|
|
// Type-specific processing for cross-service communication
|
|
switch (@event)
|
|
{
|
|
case UserAddedEvent userAdded:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Sending welcome email to {Email} (UserId: {UserId})",
|
|
userAdded.Email,
|
|
userAdded.UserId);
|
|
// In a real application:
|
|
// - Send welcome email via email service
|
|
// - Create user profile in email marketing system
|
|
// - Subscribe to mailing lists
|
|
break;
|
|
|
|
case UserRemovedEvent userRemoved:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Processing user removal for UserId: {UserId}",
|
|
userRemoved.UserId);
|
|
// In a real application:
|
|
// - Clean up user data in external systems
|
|
// - Unsubscribe from mailing lists
|
|
// - Archive user communications
|
|
break;
|
|
|
|
case UserInvitedEvent userInvited:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Sending invitation email to {Email} (InvitationId: {InvitationId})",
|
|
userInvited.Email,
|
|
userInvited.InvitationId);
|
|
// In a real application:
|
|
// - Send invitation email with link
|
|
// - Track invitation in email system
|
|
break;
|
|
|
|
case UserInviteAcceptedEvent inviteAccepted:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] User {Name} accepted invitation {InvitationId}",
|
|
inviteAccepted.Name,
|
|
inviteAccepted.InvitationId);
|
|
// In a real application:
|
|
// - Send confirmation email
|
|
// - Update CRM system
|
|
// - Trigger onboarding workflows
|
|
break;
|
|
|
|
case UserInviteDeclinedEvent inviteDeclined:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Invitation {InvitationId} was declined",
|
|
inviteDeclined.InvitationId);
|
|
// In a real application:
|
|
// - Update invitation tracking
|
|
// - Send feedback survey
|
|
break;
|
|
|
|
default:
|
|
_logger.LogInformation(
|
|
"[RABBITMQ] Received unknown event type: {EventType}",
|
|
@event.GetType().Name);
|
|
break;
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|