Designing a Scalable Notification System using .NET, Azure Service Bus, MediatR, and SignalR
Source: Dev.to
Why I’m Writing This
In one of my recent projects, we had a fairly common requirement on paper:
“Send notifications when something happens.”
But as usual, the reality was more complex:
- Notifications had to be real‑time
- System needed to scale
- Multiple consumers (email, UI, future integrations)
- And most importantly — no tight coupling
I’ve seen systems break because notifications were treated as a “side feature”. So I thought I’d share how I approached designing this properly.
The Problem with the “Simple” Approach
The first instinct is usually something like:
API → Save to DB → Send Email / NotificationLooks fine initially, but over time:
- Every feature starts calling notification logic directly
- Adding a new channel (SMS, push) becomes painful
- Failures in notification start impacting core business flow
I’ve been there — it doesn’t scale well.
How I Think About It
I try to separate concerns clearly:
- Business action happens (Order placed, Payment done)
- System emits an event
- Other parts of the system react to it
That naturally leads to an event‑driven approach.
The Architecture I Use
At a high level:
Each component has a very specific responsibility.
Where MediatR Fits
Inside the API, I use MediatR mainly for decoupling.
Instead of controllers directly calling services:
- Controller sends a command
- Handler processes it
- Business logic stays clean and testable
It also gives me a clean place to hook in things like logging, validation, etc.
Why Azure Service Bus
Once the business action is completed (e.g., order created), I don’t send notifications directly. I publish an event. That’s where Azure Service Bus comes in:
- Decouples producer and consumer
- Handles retries and failures
- Allows multiple consumers to subscribe
Today it might be just email + UI. Tomorrow it could be analytics, audit, external systems. I don’t want to rewrite my core logic for that.
SignalR for Real‑Time
For user‑facing updates, polling is not a great experience. That’s where SignalR helps:
- Server pushes updates instantly
- Works well for dashboards, alerts, chat‑like scenarios
In our case, once the notification service processes an event, it pushes updates to connected clients.
End‑to‑End Flow (How It Actually Runs)
- User places an order
- API receives the request
- MediatR handler processes and saves it
- Event gets published to Service Bus
- Notification service picks it up
- Sends email + pushes real‑time update via SignalR
Important: Order creation does not depend on notification success.
Trade‑offs (Because Nothing Is Free)
This approach is not “simple”:
- More moving parts
- Requires monitoring (Service Bus, consumers)
- Slight delay (eventual consistency)
But in return you get:
- Scalability
- Flexibility
- Cleaner architecture
For me, that trade‑off is worth it in most real systems.
Where This Works Really Well
I’ve seen this pattern work nicely in:
- E‑commerce systems
- Banking alerts
- Monitoring dashboards
Any system with user‑triggered events + multiple consumers.
Final Thoughts
One thing I’ve learned over time: notifications should be treated as a separate system, not a side‑effect.
Using MediatR, Azure Service Bus, and SignalR together helped me keep things:
- Decoupled
- Scalable
- Easier to evolve
If you’ve built something similar or approached it differently, I’d be interested to hear your thoughts. Always good to compare patterns and learn.
dotnet #azure #systemdesign #microservices #softwarearchitecture
