<?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>DEV Community: Chethan Ramaswamy</title>
    <description>The latest articles on DEV Community by Chethan Ramaswamy (@chethan_ramaswamy_773955e).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854098%2F1084dc39-c5b0-4f76-b77c-c4063d78d3f5.png</url>
      <title>DEV Community: Chethan Ramaswamy</title>
      <link>https://dev.to/chethan_ramaswamy_773955e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chethan_ramaswamy_773955e"/>
    <language>en</language>
    <item>
      <title>RAG Explained: A Beginner's Guide to Retrieval-Augmented Generation</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Sat, 26 Sep 2026 06:31:04 +0000</pubDate>
      <link>https://dev.to/chethan_ramaswamy_773955e/rag-explained-a-beginners-guide-to-retrieval-augmented-generation-2dn9</link>
      <guid>https://dev.to/chethan_ramaswamy_773955e/rag-explained-a-beginners-guide-to-retrieval-augmented-generation-2dn9</guid>
      <description>&lt;p&gt;If you've started learning &lt;strong&gt;Generative AI&lt;/strong&gt;, you've probably come across terms like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM. Embeddings. Vector Search. RAG.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first, these terms can sound complicated.&lt;/p&gt;

&lt;p&gt;But the basic idea behind &lt;strong&gt;RAG&lt;/strong&gt; is actually quite simple.&lt;/p&gt;

&lt;p&gt;Let's understand it with a real-world example. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Imagine You Have 10,000 Company Documents
&lt;/h2&gt;

&lt;p&gt;Imagine your company has thousands of documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR policies&lt;/li&gt;
&lt;li&gt;Travel policies&lt;/li&gt;
&lt;li&gt;Product documentation&lt;/li&gt;
&lt;li&gt;Technical guides&lt;/li&gt;
&lt;li&gt;Customer FAQs&lt;/li&gt;
&lt;li&gt;Security guidelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now an employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How much can I claim for a hotel during business travel?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Would you expect a general AI model to know your company's specific policy?&lt;/p&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;p&gt;The information is inside your company's documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is where RAG becomes useful.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 So, What Is RAG?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAG stands for Retrieval-Augmented Generation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of RAG like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Find the right information first, then ask the AI to answer using that information.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the basic idea.&lt;/p&gt;

&lt;p&gt;A simplified flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User asks a question
        ↓
Find relevant information
        ↓
Give that information to the AI
        ↓
AI generates an answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how this works.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏨 A Simple Real-World Example
&lt;/h2&gt;

&lt;p&gt;Suppose your company's travel policy says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Employees can claim hotel expenses up to ₹5,000 per night when traveling for business.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the hotel reimbursement limit?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A RAG application can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search the company's travel policy.&lt;/li&gt;
&lt;li&gt;Find the relevant information.&lt;/li&gt;
&lt;li&gt;Provide that information to the AI.&lt;/li&gt;
&lt;li&gt;Generate an answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI can then respond:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"According to the travel policy, employees can claim hotel expenses up to ₹5,000 per night."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice what happened?&lt;/p&gt;

&lt;p&gt;The AI didn't need to already know your company's travel policy.&lt;/p&gt;

&lt;p&gt;The application &lt;strong&gt;retrieved the relevant information first&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the basic idea behind RAG.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 How Does RAG Actually Work?
&lt;/h2&gt;

&lt;p&gt;Let's break it down into a few simple steps.&lt;/p&gt;

&lt;p&gt;Don't worry — we'll keep the technical jargon to a minimum.&lt;/p&gt;

&lt;p&gt;A basic RAG application can be understood in &lt;strong&gt;two stages&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prepare the knowledge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Answer the user's question&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at both.&lt;/p&gt;




&lt;h1&gt;
  
  
  📚 Stage 1: Prepare the Knowledge
&lt;/h1&gt;

&lt;p&gt;Before users can ask questions, we need to prepare the documents so that relevant information can be found quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  📄 Step 1: Collect Your Documents
&lt;/h3&gt;

&lt;p&gt;Your knowledge could come from many places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📄 PDF files&lt;/li&gt;
&lt;li&gt;📝 Word documents&lt;/li&gt;
&lt;li&gt;🌐 Websites&lt;/li&gt;
&lt;li&gt;🗄️ Databases&lt;/li&gt;
&lt;li&gt;☁️ Cloud storage&lt;/li&gt;
&lt;li&gt;📚 Knowledge bases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company Knowledge
       │
       ├── HR Policy.pdf
       ├── Travel Policy.pdf
       ├── Security Policy.pdf
       └── Product Manual.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These documents become the &lt;strong&gt;knowledge source&lt;/strong&gt; for our AI application.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✂️ Step 2: Break Large Documents into Smaller Pieces
&lt;/h3&gt;

&lt;p&gt;Imagine you have a &lt;strong&gt;100-page travel policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We don't want to send all 100 pages to the AI every time someone asks a question.&lt;/p&gt;

&lt;p&gt;Instead, we divide the document into smaller pieces called &lt;strong&gt;chunks&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Travel Policy
      │
      ├── Chunk 1
      ├── Chunk 2
      ├── Chunk 3
      ├── Chunk 4
      └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because when someone asks a question, we usually need only the &lt;strong&gt;small section related to that question&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
"What is the hotel reimbursement limit?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system might retrieve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 27:

"Employees can claim hotel expenses
up to ₹5,000 per night..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more useful than sending the entire document to the AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Think of it this way
&lt;/h3&gt;

&lt;p&gt;Imagine giving a student a 500-page textbook and asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find the paragraph about hotel reimbursement."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It would take time to search through the entire book.&lt;/p&gt;

&lt;p&gt;Instead, if we already know the relevant page, we can go directly there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chunking helps us create those smaller searchable pieces of information.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 Step 3: Create Embeddings
&lt;/h3&gt;

&lt;p&gt;Here's our first technical concept:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embeddings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Embeddings are numerical representations of text that help systems measure semantic similarity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Embeddings help a system find information based on semantic similarity, rather than only matching exact words.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User asks:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much can I claim for a hotel?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The document says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hotel accommodation expenses are limited to ₹5,000 per night."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The words aren't exactly the same.&lt;/p&gt;

&lt;p&gt;But the &lt;strong&gt;meaning is related&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Embeddings help the system identify this semantic similarity.&lt;/p&gt;

&lt;p&gt;You can think of it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
 ↓
Embedding Model
 ↓
Numerical Vector
 ↓
Compare with other vectors
 ↓
Find semantically similar content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to understand the mathematics behind embeddings to start learning RAG.&lt;/p&gt;

&lt;p&gt;The important thing to remember is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Embeddings help a system find information based on semantic similarity, rather than only matching exact words.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🗂️ Step 4: Store the Information
&lt;/h3&gt;

&lt;p&gt;Now we need somewhere to store our document chunks and their embeddings.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;vector databases and vector search systems&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;Some popular options include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure AI Search&lt;/li&gt;
&lt;li&gt;PostgreSQL + pgvector&lt;/li&gt;
&lt;li&gt;Pinecone&lt;/li&gt;
&lt;li&gt;Qdrant&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of this as a &lt;strong&gt;smart search system for your documents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document Chunk
      +
Embedding
      +
Metadata
      ↓
Vector Search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Metadata can contain information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document: TravelPolicy.pdf
Department: Finance
Year: 2026
DocumentType: Policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This additional information can help the application filter and organize the data.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔎 Stage 2: Answer the User's Question
&lt;/h2&gt;

&lt;p&gt;Now our knowledge has been prepared.&lt;/p&gt;

&lt;p&gt;Let's see what happens when a user asks a question.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔎 Step 5: The User Asks a Question
&lt;/h3&gt;

&lt;p&gt;The employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the hotel reimbursement limit?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application converts the question into an embedding and uses it to search the knowledge base.&lt;/p&gt;

&lt;p&gt;The search might return several results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Result 1:
Employees can claim hotel expenses
up to ₹5,000 per night.

Result 2:
Business travel expenses must be
submitted within 30 days.

Result 3:
International travel requires
manager approval.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application identifies the information that is &lt;strong&gt;most relevant to the question&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🤖 Step 6: Give the Information to the AI
&lt;/h3&gt;

&lt;p&gt;Now we have two important pieces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the hotel reimbursement limit?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Relevant information:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Employees can claim hotel expenses up to ₹5,000 per night.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application provides the question and retrieved context to the LLM.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      +
Retrieved Context
      ↓
Prompt
      ↓
LLM
      ↓
Generated Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM can then generate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Employees can claim hotel expenses up to ₹5,000 per night."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's the &lt;strong&gt;Generation&lt;/strong&gt; part of &lt;em&gt;Retrieval-Augmented Generation&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 The Complete RAG Flow
&lt;/h2&gt;

&lt;p&gt;Now let's separate the process into two simple flows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the knowledge
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents
    ↓
Chunking
    ↓
Embeddings
    ↓
Vector Search Index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Answering a question
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      ↓
Question Embedding
      ↓
Search the Knowledge Base
      ↓
Relevant Chunks
      ↓
Question + Retrieved Context
      ↓
LLM
      ↓
Final Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the heart of a RAG application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Retrieve relevant information → provide it as context → generate an answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔍 Is RAG Just Search?
&lt;/h2&gt;

&lt;p&gt;Not exactly.&lt;/p&gt;

&lt;p&gt;Traditional search might work like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Search
   ↓
List of Documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You then need to open the documents and find the answer yourself.&lt;/p&gt;

&lt;p&gt;RAG goes one step further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Search Relevant Information
   ↓
Provide Context to AI
   ↓
Generate Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can think of RAG as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Search + AI&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traditional search helps you &lt;strong&gt;find information&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A RAG application helps an AI system &lt;strong&gt;retrieve relevant information and generate an answer using that information&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🆚 RAG vs Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;This is another question beginners often have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Why not fine-tune the model instead?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;RAG and fine-tuning solve different problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RAG&lt;/th&gt;
&lt;th&gt;Fine-Tuning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retrieves external information at query time&lt;/td&gt;
&lt;td&gt;Further trains the model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful when knowledge changes frequently&lt;/td&gt;
&lt;td&gt;Useful for specialized behavior or tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents can be updated independently&lt;/td&gt;
&lt;td&gt;Requires a training process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commonly used for knowledge-based applications&lt;/td&gt;
&lt;td&gt;Commonly used for specialized model behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple way to remember it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RAG → Give the AI relevant information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fine-tuning → Adapt the AI for a specific behavior or task.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if your company's HR policy changes next month, you can update the knowledge source used by a RAG application without retraining the language model.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌎 Where Can We Use RAG?
&lt;/h2&gt;

&lt;p&gt;RAG isn't limited to company policies.&lt;/p&gt;

&lt;p&gt;It can be used in many real-world applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  👨‍💻 Developer Assistant
&lt;/h3&gt;

&lt;p&gt;A developer asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How does our authentication service work?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The RAG application searches the company's technical documentation and provides relevant context to the LLM.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏢 HR Assistant
&lt;/h3&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How many vacation days can I carry forward?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application searches HR policies and generates an answer based on the retrieved information.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ Customer Support
&lt;/h3&gt;

&lt;p&gt;A customer asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I reset my device?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application can search product documentation and provide step-by-step guidance.&lt;/p&gt;




&lt;h3&gt;
  
  
  📚 Enterprise Knowledge Assistant
&lt;/h3&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the process for raising a purchase request?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application searches company procedures and generates a response.&lt;/p&gt;

&lt;p&gt;The common pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company Knowledge
       ↓
      RAG
       ↓
      LLM
       ↓
  AI Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚠️ Does RAG Make AI 100% Accurate?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is important to understand.&lt;/p&gt;

&lt;p&gt;RAG can provide the AI with relevant information, but it does &lt;strong&gt;not guarantee a perfect answer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, if the retrieval system finds the wrong information, the LLM may generate an incorrect response based on that context.&lt;/p&gt;

&lt;p&gt;There can also be problems if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source document contains incorrect information.&lt;/li&gt;
&lt;li&gt;Important information was not retrieved.&lt;/li&gt;
&lt;li&gt;The retrieved context is incomplete.&lt;/li&gt;
&lt;li&gt;The prompt doesn't clearly guide the model.&lt;/li&gt;
&lt;li&gt;The model misunderstands the retrieved context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why production RAG systems need good:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document processing&lt;/li&gt;
&lt;li&gt;Search and retrieval&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Prompt design&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So don't think of RAG as a magic solution.&lt;/p&gt;

&lt;p&gt;Instead, think of it as a way to &lt;strong&gt;ground an AI application with relevant external information&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ☁️ What Does RAG Look Like with .NET and Azure?
&lt;/h2&gt;

&lt;p&gt;If you're a .NET developer, a simple enterprise RAG architecture could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 User
                   ↓
              .NET Web API
                   ↓
             Azure AI Search
                   ↓
             Relevant Chunks
                   ↓
             Prompt + Context
                   ↓
             Azure OpenAI
                   ↓
              AI Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your .NET application can orchestrate the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive the user's question.&lt;/li&gt;
&lt;li&gt;Search for relevant information.&lt;/li&gt;
&lt;li&gt;Retrieve the relevant chunks.&lt;/li&gt;
&lt;li&gt;Build the prompt with the retrieved context.&lt;/li&gt;
&lt;li&gt;Send the request to the AI model.&lt;/li&gt;
&lt;li&gt;Return the response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes RAG particularly interesting for developers working with &lt;strong&gt;.NET and Azure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The 5 Things You Need to Remember
&lt;/h2&gt;

&lt;p&gt;If you're completely new to RAG, remember these five concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documents&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Where your knowledge lives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunks&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Smaller pieces of your documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embeddings&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Numerical representations that help systems measure semantic similarity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Finding information relevant to the user's question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The LLM uses the retrieved information to generate the answer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put them together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents
    ↓
Chunks
    ↓
Embeddings
    ↓
Search
    ↓
Relevant Context
    ↓
LLM
    ↓
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Your Turn
&lt;/h2&gt;

&lt;p&gt;Have you tried building a RAG application?&lt;/p&gt;

&lt;p&gt;What would you like to build with RAG?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👨‍💻 Developer assistant&lt;/li&gt;
&lt;li&gt;📄 Document chatbot&lt;/li&gt;
&lt;li&gt;🏢 Enterprise knowledge assistant&lt;/li&gt;
&lt;li&gt;🛠️ Customer support assistant&lt;/li&gt;
&lt;li&gt;🤖 Something else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Share your idea in the comments. 👇&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Key Takeaway
&lt;/h2&gt;

&lt;p&gt;If you remember just one sentence from this article, remember this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A RAG application retrieves relevant information from a knowledge source and provides it to an LLM so it can generate a more informed response.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's the foundation of many modern &lt;strong&gt;Generative AI applications&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>generativeai</category>
      <category>rag</category>
    </item>
    <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://dev.to/chethan_ramaswamy_773955e/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</link>
      <guid>https://dev.to/chethan_ramaswamy_773955e/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</guid>
      <description>&lt;h1&gt;
  
  
  Designing a Scalable Notification System using .NET, Azure Service Bus, MediatR, and SignalR
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why I'm Writing This
&lt;/h2&gt;

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

&lt;p&gt;It sounds simple.&lt;/p&gt;

&lt;p&gt;But in a real application, notifications can quickly become more complicated than expected.&lt;/p&gt;

&lt;p&gt;You may need to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time UI notifications&lt;/li&gt;
&lt;li&gt;Email and SMS&lt;/li&gt;
&lt;li&gt;Multiple consumers&lt;/li&gt;
&lt;li&gt;Retries and failure handling&lt;/li&gt;
&lt;li&gt;High traffic&lt;/li&gt;
&lt;li&gt;Future integrations&lt;/li&gt;
&lt;li&gt;Loose coupling between business logic and notification processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is not simply &lt;strong&gt;how to send a notification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we design notifications so they don't become tightly coupled to the core business flow?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article walks through one approach using &lt;strong&gt;.NET, MediatR, Azure Service Bus, SignalR, and the Outbox Pattern&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem with the "Simple" Approach
&lt;/h1&gt;

&lt;p&gt;A common implementation starts with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API
  ↓
Save to Database
  ↓
Send Email
  ↓
Send Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;At least initially.&lt;/p&gt;

&lt;p&gt;But as the application grows, problems start appearing.&lt;/p&gt;

&lt;p&gt;Every feature begins calling notification logic directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService → NotificationService
PaymentService → NotificationService
UserService → NotificationService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then new requirements arrive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add SMS&lt;/li&gt;
&lt;li&gt;Add push notifications&lt;/li&gt;
&lt;li&gt;Add in-app notifications&lt;/li&gt;
&lt;li&gt;Add analytics&lt;/li&gt;
&lt;li&gt;Add audit events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notification service gradually becomes tightly coupled to multiple parts of the application.&lt;/p&gt;

&lt;p&gt;Another problem is failure handling.&lt;/p&gt;

&lt;p&gt;What happens if the email provider is temporarily unavailable?&lt;/p&gt;

&lt;p&gt;Should the order creation fail?&lt;/p&gt;

&lt;p&gt;Usually, the answer is &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The business operation and notification delivery have different reliability requirements.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Better Approach: Event-Driven Notifications
&lt;/h1&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Business Action
      ↓
Domain/Application Event
      ↓
Outbox
      ↓
Message Broker
      ↓
Notification Consumers
      ↓
Email / UI / SMS / Push / etc.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The business operation completes without waiting for every notification channel to finish.&lt;/p&gt;

&lt;p&gt;This creates a much cleaner boundary between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Something happened"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What should we do because it happened?"&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  High-Level Architecture
&lt;/h1&gt;

&lt;p&gt;The production-oriented architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         ┌──────────────────┐
                         │      Client      │
                         └────────┬─────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │   .NET Web API   │
                         └────────┬─────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │     MediatR      │
                         │ Command Handler  │
                         └────────┬─────────┘
                                  │
                                  ▼
                     ┌──────────────────────────┐
                     │    Database Transaction  │
                     │                          │
                     │  ┌────────┐ ┌─────────┐ │
                     │  │ Order  │ │ Outbox  │ │
                     │  └────────┘ └─────────┘ │
                     └────────────┬─────────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │ Outbox Processor │
                         └────────┬─────────┘
                                  │
                                  ▼
                     ┌────────────────────────┐
                     │  Azure Service Bus     │
                     │        Topic           │
                     └────────────┬───────────┘
                                  │
                   ┌──────────────┼──────────────┐
                   │              │              │
                   ▼              ▼              ▼
             ┌──────────┐   ┌───────────┐  ┌──────────┐
             │  Email   │   │  SignalR  │  │  Audit   │
             │ Consumer │   │  Consumer │  │ Consumer │
             └──────────┘   └─────┬─────┘  └──────────┘
                                  │
                                  ▼
                     ┌────────────────────────┐
                     │ Azure SignalR Service  │
                     └────────────┬───────────┘
                                  │
                                  ▼
                           ┌─────────────┐
                           │   Clients   │
                           │Browser/Mobile│
                           └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important principle is that each component has a specific responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Outbox is part of the primary architecture, not an afterthought.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Where MediatR Fits
&lt;/h1&gt;

&lt;p&gt;MediatR sits primarily inside the application layer.&lt;/p&gt;

&lt;p&gt;Instead of having a controller directly coordinate business operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
Service
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can use a request/handler model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
MediatR
    ↓
Command Handler
    ↓
Business Logic
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler processes the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderHandler&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validate business rules&lt;/span&gt;
        &lt;span class="c1"&gt;// Create order&lt;/span&gt;
        &lt;span class="c1"&gt;// Save order&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MediatR also provides a clean place for application-level cross-cutting concerns such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Performance measurement&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Transaction behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important point is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;MediatR is not the message broker.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It helps with &lt;strong&gt;in-process application decoupling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Azure Service Bus handles &lt;strong&gt;distributed messaging&lt;/strong&gt; between processes or services.&lt;/p&gt;

&lt;p&gt;That distinction becomes important as the system grows.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Azure Service Bus?
&lt;/h1&gt;

&lt;p&gt;After the business operation is committed, an event such as &lt;code&gt;OrderCreated&lt;/code&gt; needs to be delivered to downstream consumers.&lt;/p&gt;

&lt;p&gt;With the Outbox Pattern, the event is first persisted as part of the same database transaction and is then published asynchronously.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Outbox
     ↓
Outbox Processor
     ↓
Azure Service Bus
     ↓
Notification Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Azure Service Bus provides the messaging infrastructure between the producer and consumers.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Notification Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Outbox
     ↓
Azure Service Bus
     ↓
Notification Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides several benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Loose Coupling
&lt;/h2&gt;

&lt;p&gt;The producer doesn't need to know which consumers exist.&lt;/p&gt;

&lt;p&gt;Today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
    ↓
Email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tomorrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
    ↓
Email
SMS
Analytics
Audit
Push Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order-processing logic doesn't need to change simply because another consumer was added.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Retry and Failure Handling
&lt;/h2&gt;

&lt;p&gt;Azure Service Bus provides messaging and delivery mechanisms, while retry behavior is typically implemented through the Service Bus client and consumer/application logic.&lt;/p&gt;

&lt;p&gt;Transient failures can therefore be handled without making the original business transaction depend on downstream notification delivery.&lt;/p&gt;

&lt;p&gt;Messages that repeatedly fail processing can also be moved to a &lt;strong&gt;dead-letter queue&lt;/strong&gt; for investigation or later recovery.&lt;/p&gt;

&lt;p&gt;The exact retry strategy should be designed at the consumer level based on the type of failure.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
Consumer
   ↓
Processing succeeds?
   ├── Yes → Complete message
   │
   └── No
       ↓
   Retry / Redelivery
       ↓
   Repeated failure?
       └── Dead-letter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Independent Scaling
&lt;/h2&gt;

&lt;p&gt;The notification consumers can scale independently from the API.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API instances
     ↓
Outbox Processor
     ↓
Service Bus
     ↓
Notification Workers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If notification volume increases significantly, the consumer side can be scaled independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Future Integrations
&lt;/h2&gt;

&lt;p&gt;The same event can potentially be consumed by additional systems without modifying the original producer.&lt;/p&gt;

&lt;p&gt;This is particularly useful in larger enterprise environments.&lt;/p&gt;




&lt;h1&gt;
  
  
  Topics and Subscriptions
&lt;/h1&gt;

&lt;p&gt;For multiple consumers, an Azure Service Bus &lt;strong&gt;Topic&lt;/strong&gt; with multiple &lt;strong&gt;Subscriptions&lt;/strong&gt; is a natural fit.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  OrderCreated Event
                         │
                         ▼
                ┌─────────────────┐
                │ Azure Service   │
                │ Bus Topic       │
                └────────┬────────┘
                         │
             ┌───────────┼───────────┐
             │           │           │
             ▼           ▼           ▼
        Email Sub    SignalR Sub   Audit Sub
             │           │           │
             ▼           ▼           ▼
          Email       SignalR       Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subscription can receive its own copy of the message.&lt;/p&gt;

&lt;p&gt;This keeps consumers independent.&lt;/p&gt;

&lt;p&gt;It also means that one consumer can fail or be delayed without preventing another subscription from processing its own copy of the event.&lt;/p&gt;




&lt;h1&gt;
  
  
  SignalR for Real-Time Notifications
&lt;/h1&gt;

&lt;p&gt;For browser-based real-time notifications, polling can introduce unnecessary latency and traffic.&lt;/p&gt;

&lt;p&gt;SignalR allows the server to push messages to connected clients.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated Event
        ↓
SignalR Consumer
        ↓
SignalR Hub
        ↓
Azure SignalR Service
        ↓
Connected Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client can receive something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Order #12345 has been created successfully."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works particularly well for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Order tracking&lt;/li&gt;
&lt;li&gt;Monitoring systems&lt;/li&gt;
&lt;li&gt;Alerts&lt;/li&gt;
&lt;li&gt;Chat-like applications&lt;/li&gt;
&lt;li&gt;In-app notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A SignalR connection can also be associated with a user or group, allowing notifications to be targeted appropriately.&lt;/p&gt;




&lt;h1&gt;
  
  
  SignalR at Scale
&lt;/h1&gt;

&lt;p&gt;A single SignalR server is straightforward.&lt;/p&gt;

&lt;p&gt;The challenge appears when the application is deployed across multiple instances.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       ┌──────────────────────┐
                       │   Azure SignalR      │
                       │      Service         │
                       └──────────┬───────────┘
                                  │
                    ┌─────────────┼─────────────┐
                    │             │             │
                    ▼             ▼             ▼
               API Instance   API Instance   API Instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Azure-hosted applications, &lt;strong&gt;Azure SignalR Service&lt;/strong&gt; can provide the scale-out layer for SignalR connections.&lt;/p&gt;

&lt;p&gt;This allows application instances to remain focused on application processing while Azure SignalR Service manages persistent client connections.&lt;/p&gt;

&lt;p&gt;The exact deployment model depends on application scale, hosting model, and operational requirements.&lt;/p&gt;




&lt;h1&gt;
  
  
  End-to-End Flow
&lt;/h1&gt;

&lt;p&gt;Let's put everything together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — User performs an action
&lt;/h2&gt;

&lt;p&gt;The user creates an order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → POST /orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — API receives the request
&lt;/h2&gt;

&lt;p&gt;The controller sends a command through MediatR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
CreateOrderCommand
    ↓
MediatR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Command handler processes the business operation
&lt;/h2&gt;

&lt;p&gt;The handler validates the request and creates the order.&lt;/p&gt;

&lt;p&gt;The order and the corresponding outbox event are persisted in the same database transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database Transaction
       │
       ├── Order
       │
       └── Outbox Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is important because it prevents the application from successfully committing the order while completely losing the event because of a separate publish operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Outbox processor publishes the event
&lt;/h2&gt;

&lt;p&gt;A background process reads unpublished events from the outbox and publishes them to Azure Service Bus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
   ↓
Outbox Processor
   ↓
Azure Service Bus Topic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The business transaction does not wait for the downstream consumers to finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Consumers process the event
&lt;/h2&gt;

&lt;p&gt;Different consumers can react independently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 OrderCreated
                      ↓
              Azure Service Bus
                 /     |      \
                /      |       \
               ▼       ▼        ▼
            Email   SignalR    Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6 — Real-time update reaches the client
&lt;/h2&gt;

&lt;p&gt;The SignalR consumer sends an update to the appropriate connected client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Service Bus
     ↓
SignalR Consumer
     ↓
SignalR Hub
     ↓
Azure SignalR Service
     ↓
Browser / Mobile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Order creation should not depend on notification delivery succeeding.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  One Important Production Consideration: The Outbox Pattern
&lt;/h1&gt;

&lt;p&gt;There is a subtle issue with a simpler implementation.&lt;/p&gt;

&lt;p&gt;Consider this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Save Order
2. Publish OrderCreated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens if step 1 succeeds but step 2 fails?&lt;/p&gt;

&lt;p&gt;The order exists in the database, but the event was never published.&lt;/p&gt;

&lt;p&gt;This is a classic &lt;strong&gt;dual-write problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For production systems where reliable event publishing is important, an &lt;strong&gt;Outbox Pattern&lt;/strong&gt; can help.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Save Order
     ↓
Publish Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we persist both the business data and the outgoing event in the same database transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database Transaction
       │
       ├── Order
       │
       └── Outbox Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A background worker then reads the outbox and publishes events to Service Bus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
   ↓
Outbox Processor
   ↓
Azure Service Bus
   ↓
Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates the critical business transaction from downstream message delivery while preserving the event that needs to be published.&lt;/p&gt;




&lt;h1&gt;
  
  
  Handling Duplicate Messages
&lt;/h1&gt;

&lt;p&gt;Messaging systems should generally be designed with the assumption that a message can potentially be delivered more than once.&lt;/p&gt;

&lt;p&gt;Azure Service Bus commonly uses &lt;strong&gt;at-least-once delivery semantics&lt;/strong&gt; with Peek-Lock processing, so consumers should be designed to handle duplicate delivery safely.&lt;/p&gt;

&lt;p&gt;That means consumers should ideally be &lt;strong&gt;idempotent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message ID: 8f23...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer can maintain a record of processed message IDs.&lt;/p&gt;

&lt;p&gt;If the same message arrives again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
Already processed?
   ├── Yes → Ignore / safely return
   └── No  → Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially important when processing operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Updating notification state&lt;/li&gt;
&lt;li&gt;Creating records&lt;/li&gt;
&lt;li&gt;Calling external APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Azure Service Bus also provides &lt;strong&gt;duplicate detection&lt;/strong&gt; capabilities, but duplicate detection should not be treated as a replacement for idempotent consumer design.&lt;/p&gt;




&lt;h1&gt;
  
  
  Trade-Offs
&lt;/h1&gt;

&lt;p&gt;This architecture is not free.&lt;/p&gt;

&lt;p&gt;You are introducing additional infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure Service Bus&lt;/li&gt;
&lt;li&gt;Outbox storage&lt;/li&gt;
&lt;li&gt;Background consumers&lt;/li&gt;
&lt;li&gt;SignalR / Azure SignalR Service&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Retry handling&lt;/li&gt;
&lt;li&gt;Dead-letter handling&lt;/li&gt;
&lt;li&gt;Distributed tracing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also eventual consistency.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Created
      ↓
Outbox Event
      ↓
Event Published
      ↓
Consumer Processes Event
      ↓
Notification Appears
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notification might appear milliseconds or seconds after the business operation.&lt;/p&gt;

&lt;p&gt;But that trade-off can be worthwhile when the system needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Resilience&lt;/li&gt;
&lt;li&gt;Independent consumers&lt;/li&gt;
&lt;li&gt;Extensibility&lt;/li&gt;
&lt;li&gt;Loose coupling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture should still be evaluated against the actual requirements.&lt;/p&gt;

&lt;p&gt;A small application with one notification channel may not need all of these components.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where This Architecture Works Well
&lt;/h1&gt;

&lt;p&gt;This pattern can be useful in systems such as:&lt;/p&gt;

&lt;h2&gt;
  
  
  E-commerce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderPlaced
    ↓
Email
Inventory
Payment
Shipping
Customer Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Banking
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TransactionCompleted
    ↓
SMS
Email
Mobile Push
Audit
Fraud Monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring Platforms
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AlertRaised
    ↓
Dashboard
Email
SMS
Incident Management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enterprise Applications
&lt;/h2&gt;

&lt;p&gt;Any system where a business event may need to trigger multiple independent downstream actions can benefit from this approach.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Design Principles
&lt;/h1&gt;

&lt;p&gt;The technology is important, but the architectural principles matter more.&lt;/p&gt;

&lt;h3&gt;
  
  
  MediatR
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;in-process application decoupling&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Service Bus
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;asynchronous communication between distributed components&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Service Bus Topics and Subscriptions
&lt;/h3&gt;

&lt;p&gt;Use them when multiple independent consumers need to react to the same event.&lt;/p&gt;

&lt;h3&gt;
  
  
  SignalR
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;real-time communication with connected clients&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure SignalR Service
&lt;/h3&gt;

&lt;p&gt;Consider it when SignalR needs to scale across multiple application instances in Azure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outbox Pattern
&lt;/h3&gt;

&lt;p&gt;Use it when you need stronger guarantees between &lt;strong&gt;database state and event publishing&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotent Consumers
&lt;/h3&gt;

&lt;p&gt;Design consumers so duplicate messages don't create unintended side effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dead-Letter Handling
&lt;/h3&gt;

&lt;p&gt;Treat repeatedly failed messages as operational data that needs monitoring, investigation, and potentially controlled replay.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Notifications are easy to implement when a system is small.&lt;/p&gt;

&lt;p&gt;They become much more interesting when the application needs to scale, support multiple channels, handle failures, and evolve over time.&lt;/p&gt;

&lt;p&gt;A useful separation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Business Logic
      ↓
Database Transaction
      ↓
Outbox Event
      ↓
Message Broker
      ↓
Independent Consumers
      ↓
Notification Channels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this approach, the core business operation doesn't need to know whether the notification is delivered through email, SignalR, SMS, push notifications, or a future integration.&lt;/p&gt;

&lt;p&gt;The result is an architecture that is more &lt;strong&gt;decoupled, scalable, resilient, and easier to evolve&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And perhaps the most important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't let notification delivery become part of your critical business transaction unless it truly needs to be.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've implemented a similar architecture, I'd be interested in hearing how you handled retries, ordering, duplicate messages, dead-letter queues, and the outbox problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  dotnet #azure #systemdesign #microservices #softwarearchitecture #azureservicebus #signalr #mediatr #eventdrivenarchitecture
&lt;/h1&gt;

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