<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Chethan Ramaswamy</title>
    <description>The latest articles on Forem by Chethan Ramaswamy (@chethan_ramaswamy_773955e).</description>
    <link>https://forem.com/chethan_ramaswamy_773955e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854098%2F1084dc39-c5b0-4f76-b77c-c4063d78d3f5.png</url>
      <title>Forem: Chethan Ramaswamy</title>
      <link>https://forem.com/chethan_ramaswamy_773955e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chethan_ramaswamy_773955e"/>
    <language>en</language>
    <item>
      <title>Designing a Scalable Notification System using .NET, Azure Service Bus, MediatR, and SignalR</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Tue, 31 Mar 2026 19:35:49 +0000</pubDate>
      <link>https://forem.com/chethan_ramaswamy_773955e/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</link>
      <guid>https://forem.com/chethan_ramaswamy_773955e/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</guid>
      <description>&lt;p&gt;&lt;strong&gt;Why I’m Writing This&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In one of my recent projects, we had a fairly common requirement on paper:&lt;/p&gt;

&lt;p&gt;“Send notifications when something happens.”&lt;/p&gt;

&lt;p&gt;But as usual, the reality was more complex:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notifications had to be real-time&lt;/li&gt;
&lt;li&gt;System needed to scale&lt;/li&gt;
&lt;li&gt;Multiple consumers (email, UI, future integrations)&lt;/li&gt;
&lt;li&gt;And most importantly — no tight coupling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem with the “Simple” Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first instinct is usually something like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;API → Save to DB → Send Email / Notification&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Looks fine initially. But over time:&lt;/p&gt;

&lt;p&gt;Every feature starts calling notification logic directly&lt;br&gt;
Adding a new channel (SMS, push) becomes painful&lt;br&gt;
Failures in notification start impacting core business flow&lt;/p&gt;

&lt;p&gt;I’ve been there — it doesn’t scale well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I Think About It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I try to separate concerns clearly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Business action happens (Order placed, Payment done)&lt;/li&gt;
&lt;li&gt;System emits an event&lt;/li&gt;
&lt;li&gt;Other parts of the system react to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That naturally leads to an event-driven approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Architecture I Use&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At a high level:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3jen8qr5tvrf5z8aaaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3jen8qr5tvrf5z8aaaj.png" alt=" " width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each component has a very specific responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where MediatR Fits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the API, I use MediatR mainly for decoupling.&lt;/p&gt;

&lt;p&gt;Instead of controllers directly calling services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Controller sends a command&lt;/li&gt;
&lt;li&gt;Handler processes it&lt;/li&gt;
&lt;li&gt;Business logic stays clean and testable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It also gives me a clean place to hook in things like logging, validation, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Azure Service Bus&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the business action is completed (say, order created), I don’t send notifications directly.&lt;/p&gt;

&lt;p&gt;I publish an event.&lt;/p&gt;

&lt;p&gt;That’s where Azure Service Bus comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decouples producer and consumer&lt;/li&gt;
&lt;li&gt;Handles retries, failures&lt;/li&gt;
&lt;li&gt;Lets multiple consumers subscribe&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today it might be just email + UI. Tomorrow it could be analytics, audit, external systems.&lt;/p&gt;

&lt;p&gt;I don’t want to rewrite my core logic for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SignalR for Real-Time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For user-facing updates, polling is not a great experience.&lt;/p&gt;

&lt;p&gt;That’s where SignalR helps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server pushes updates instantly&lt;/li&gt;
&lt;li&gt;Works well for dashboards, alerts, chat-like scenarios&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our case, once the notification service processes an event, it pushes updates to connected clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;End-to-End Flow (How It Actually Runs)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User places an order&lt;/li&gt;
&lt;li&gt;API receives request&lt;/li&gt;
&lt;li&gt;MediatR handler processes and saves it&lt;/li&gt;
&lt;li&gt;Event gets published to Service Bus&lt;/li&gt;
&lt;li&gt;Notification service picks it up&lt;/li&gt;
&lt;li&gt;Sends email + pushes real-time update via SignalR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important part:&lt;br&gt;
👉 Order creation does not depend on notification success&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs (Because Nothing Is Free)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This approach is not “simple”:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;More moving parts&lt;/li&gt;
&lt;li&gt;Requires monitoring (Service Bus, consumers)&lt;/li&gt;
&lt;li&gt;Slight delay (eventual consistency)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But in return, you get:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Flexibility&lt;/li&gt;
&lt;li&gt;Cleaner architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For me, that trade-off is worth it in most real systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where This Works Really Well&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve seen this pattern work nicely in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;E-commerce systems&lt;/li&gt;
&lt;li&gt;Banking alerts&lt;/li&gt;
&lt;li&gt;Monitoring dashboards&lt;/li&gt;
&lt;li&gt;Any system with user-triggered events + multiple consumers
&lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing I’ve learned over time:&lt;/p&gt;

&lt;p&gt;Notifications should be treated as a separate system, not a side-effect.&lt;/p&gt;

&lt;p&gt;Using MediatR, Service Bus, and SignalR together helped me keep things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decoupled&lt;/li&gt;
&lt;li&gt;Scalable&lt;/li&gt;
&lt;li&gt;Easier to evolve&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h1&gt;
  
  
  dotnet #azure #systemdesign #microservices #softwarearchitecture
&lt;/h1&gt;

</description>
      <category>systemdesign</category>
      <category>dotnet</category>
      <category>azure</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
