Course contentsShow
← C#
Lesson 1482 of 1,90813. Real-time Communication with SignalRFree lesson

Event-Driven Notifications Engine

Develop a system to dispatch high-priority system events to active clients in real-time.

In this lesson, you will learn how to architect a decoupled event-driven notification engine that broadcasts critical system alerts to SignalR clients without tightly coupling your core business logic to the Hub.

The Decoupled Architecture

At this advanced stage, your core services (like billing or hardware monitoring) should not know that SignalR exists. Instead, we use an internal event bus (or MediatR pattern). When a high-priority event occurs, the service publishes a notification to a mediator, which the Hub (or a background worker) consumes to push updates to specific clients.

Implementation Pattern

To handle high-priority events, we avoid calling Clients.All directly inside business services. Instead, we use a background worker that acts as a bridge.

// 1. The Domain Event
public record SystemAlertEvent(string Message, Severity Level);

// 2. The Hub acting as the Subscriber
public class NotificationHub : Hub { }

// 3. The Background Service subscribing to events
public class NotificationDispatcher : BackgroundService 
{
    private readonly IHubContext<NotificationHub> _hubContext;
    private readonly IEventBus _eventBus; // Internal system bus

    public NotificationDispatcher(IHubContext<NotificationHub> hubContext, IEventBus eventBus)
    {
        _hubContext = hubContext;
        _eventBus = eventBus;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _eventBus.Subscribe<SystemAlertEvent>(async alert => 
        {
            if (alert.Level == Severity.Critical)
            {
                // Dispatching to the specific group or all active clients
                await _hubContext.Clients.All.SendAsync("ReceiveAlert", alert.Message);
            }
        });
    }
}

Why this matters

By separating the notification triggering from the transport layer, you gain several advantages:

  • Scalability: You can easily swap the IEventBus implementation to use a message broker like RabbitMQ if your system grows across multiple server nodes.
  • Testability: You can unit test your business logic by asserting that a SystemAlertEvent was published, without mocking the entire SignalR IHubContext.
  • Reliability: Since the dispatching is handled by a background service, you can implement retry logic or queuing for notifications that must be delivered even if the SignalR transport flickers.

Key Takeaway: Decouple your business domain from SignalR by using an internal event bus to bridge system events to a background dispatcher, ensuring your notification logic remains scalable and independently testable.