<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://mahedee.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mahedee.net/" rel="alternate" type="text/html" /><updated>2026-03-25T19:26:41-04:00</updated><id>https://mahedee.net/feed.xml</id><title type="html">Think Simple</title><subtitle>Think Simple</subtitle><author><name>Mahedee Hasan</name></author><entry><title type="html">Securing an ASP.NET Core Web API by Integrating Azure Key Vault</title><link href="https://mahedee.net/securing-aspnet-web-api-app-using-azure-key-vault/" rel="alternate" type="text/html" title="Securing an ASP.NET Core Web API by Integrating Azure Key Vault" /><published>2026-03-07T00:00:00-05:00</published><updated>2026-03-07T00:00:00-05:00</updated><id>https://mahedee.net/securing-aspnet-web-api-app-using-azure-key-vault</id><content type="html" xml:base="https://mahedee.net/securing-aspnet-web-api-app-using-azure-key-vault/"><![CDATA[<p>Azure Key Vault is a Microsoft Azure service for securely storing and managing sensitive information — secrets (passwords, connection strings, API keys), cryptographic keys, and certificates. Instead of hardcoding secrets in config files or source code, your application fetches them from the vault at runtime. Access is controlled via Azure RBAC, every read is audit-logged, and services running in Azure can authenticate using Managed Identity — with zero credentials stored anywhere.</p>

<h2 id="what-well-build">What We’ll Build</h2>

<p>We’ll create a simple e-commerce ASP.NET Core Web API called ECommerceApi that uses Azure Key Vault to securely manage all sensitive configuration at runtime — no secrets stored in code or config files.</p>

<p>The application includes:</p>

<ul>
  <li>Auth — user registration and login with JWT bearer tokens</li>
  <li>Products — full CRUD for the product catalog</li>
  <li>Orders — order placement with stock management and email confirmation</li>
  <li>Config endpoint — development-only endpoint to verify all Key Vault secrets are loaded correctly</li>
</ul>

<p>All sensitive values — the SQL Server connection string, JWT signing secret, and SMTP credentials — are stored as secrets in Azure Key Vault and loaded automatically into IConfiguration at startup using DefaultAzureCredential.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>Before we start, make sure you have:</p>
<ul>
  <li>.NET 10 SDK</li>
  <li>Azure CLI</li>
  <li>SQL Server (local or Azure)</li>
  <li>Azure subscription</li>
  <li>Visual Studio 2026 or Visual Studio Code</li>
</ul>

<h2 id="step-1-create-a-resource-group">Step 1: Create a Resource Group</h2>

<ol>
  <li>Go to <a href="https://portal.azure.com">https://portal.azure.com</a> and sign in.</li>
  <li>In the top search bar, type <strong>Resource groups</strong> and click it.</li>
  <li>Click <strong>+ Create</strong>.</li>
  <li>Fill in:
    <ul>
      <li><strong>Subscription</strong> → Select your subscription</li>
      <li><strong>Resource group</strong> → <code class="language-plaintext highlighter-rouge">rg-ecommerce-dev</code></li>
      <li><strong>Region</strong> → Choose the region closest to you (e.g., <code class="language-plaintext highlighter-rouge">West US</code>)</li>
    </ul>
  </li>
  <li>Click <strong>Review + create</strong> → <strong>Create</strong>.</li>
</ol>

<h2 id="step-2-create-the-azure-key-vault">Step 2: Create the Azure Key Vault</h2>

<ol>
  <li>In the Azure Portal search bar, type <strong>Key vaults</strong> and click it.</li>
  <li>Click <strong>+ Create</strong>.</li>
  <li>
    <p>Fill in the <strong>Basics</strong> tab:</p>

    <table>
      <thead>
        <tr>
          <th>Field</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Subscription</td>
          <td>Your subscription</td>
        </tr>
        <tr>
          <td>Resource group</td>
          <td><code class="language-plaintext highlighter-rouge">rg-ecommerce-dev</code></td>
        </tr>
        <tr>
          <td>Key vault name</td>
          <td><code class="language-plaintext highlighter-rouge">kv-ecommerce-dev-mh</code> <em>(must be globally unique)</em></td>
        </tr>
        <tr>
          <td>Region</td>
          <td>Same as your resource group</td>
        </tr>
        <tr>
          <td>Pricing tier</td>
          <td><strong>Standard</strong></td>
        </tr>
      </tbody>
    </table>
  </li>
  <li>Click <strong>Next: Access configuration</strong>.</li>
  <li>On the <strong>Access configuration</strong> tab:
    <ul>
      <li><strong>Permission model</strong> → Select <strong>Azure role-based access control (RBAC)</strong><br />
<em>(RBAC is recommended over Vault Access Policies for better auditability)</em></li>
    </ul>
  </li>
  <li>Leave all other settings as default.</li>
  <li>Click <strong>Review + create</strong> → <strong>Create</strong>.</li>
  <li>Once deployed, click <strong>Go to resource</strong>.</li>
  <li><strong>Copy the Vault URI</strong> from the Overview page — it looks like:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  https://kv-ecommerce-dev-mh.vault.azure.net/
</code></pre></div>    </div>
    <p>You will paste this into <code class="language-plaintext highlighter-rouge">appsettings.json</code>.</p>

    <p><img src="/assets/images/posts/2026/create-kv.png" alt="" />
<strong>Fig : Creating the Azure Key Vault</strong>
 
 
<img src="/assets/images/posts/2026/vault-URI.png" alt="" />
 <strong>Fig : Copying the Vault URI</strong></p>
  </li>
</ol>

<h2 id="step-3-grant-access--local-development">Step 3: Grant Access — Local Development</h2>

<p>When running locally, <code class="language-plaintext highlighter-rouge">DefaultAzureCredential</code> uses your <code class="language-plaintext highlighter-rouge">az login</code> session.<br />
You need to assign your own Azure AD account the <strong>Key Vault Secrets User</strong> role.</p>

<p><strong>Log in to Azure CLI</strong></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">az</span><span class="w"> </span><span class="nx">login</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">account</span><span class="w"> </span><span class="nx">show</span><span class="w">   </span><span class="c"># verify the correct subscription is active</span><span class="w">
</span></code></pre></div></div>

<p>If you have multiple subscriptions:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">az</span><span class="w"> </span><span class="nx">account</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nt">--subscription</span><span class="w"> </span><span class="s2">"&lt;your-subscription-id&gt;"</span><span class="w">
</span></code></pre></div></div>

<p><strong>Assign yourself the required roles</strong></p>

<p>You need <strong>two roles</strong> assigned to your own Azure AD account:</p>

<table>
  <thead>
    <tr>
      <th>Role</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Key Vault Secrets Officer</strong></td>
      <td>Create, update, delete secrets via the Portal or CLI</td>
    </tr>
    <tr>
      <td><strong>Key Vault Secrets User</strong></td>
      <td>Read secrets at runtime (also used by the app’s Managed Identity)</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><strong>Important:</strong> Without <strong>Key Vault Secrets Officer</strong>, you will get a <code class="language-plaintext highlighter-rouge">Forbidden / RBAC</code> error when trying to create secrets in the Portal. This is separate from the runtime read access the app needs.</p>
</blockquote>

<p><strong>Steps (repeat for each role):</strong></p>

<ol>
  <li>In the Azure Portal, navigate to your Key Vault (<code class="language-plaintext highlighter-rouge">kv-ecommerce-dev-mh</code>).</li>
  <li>Click <strong>Access control (IAM)</strong> in the left menu.</li>
  <li>Click <strong>+ Add → Add role assignment</strong>.</li>
  <li>On the <strong>Role</strong> tab:
    <ul>
      <li>Search for and select the role (<strong>Key Vault Secrets Officer</strong> first, then repeat for <strong>Key Vault Secrets User</strong>)</li>
      <li>Click <strong>Next</strong></li>
    </ul>
  </li>
  <li>On the <strong>Members</strong> tab:
    <ul>
      <li><strong>Assign access to</strong> → <code class="language-plaintext highlighter-rouge">User, group, or service principal</code></li>
      <li>Click <strong>+ Select members</strong></li>
      <li>Search for your own Azure AD email/name → Select it</li>
      <li>Click <strong>Select</strong> → <strong>Next</strong> → <strong>Review + assign</strong></li>
    </ul>
  </li>
  <li><strong>Wait 2–5 minutes</strong> for the role to propagate before creating secrets.</li>
</ol>

<p><strong>Or assign both roles at once via CLI:</strong></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$myObjectId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">az</span><span class="w"> </span><span class="nx">ad</span><span class="w"> </span><span class="nx">signed-in-user</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">--query</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="nt">-o</span><span class="w"> </span><span class="nx">tsv</span><span class="w">
</span><span class="nv">$scope</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/subscriptions/&lt;your-subscription-id&gt;/resourceGroups/rg-ecommerce-dev/providers/Microsoft.KeyVault/vaults/kv-ecommerce-dev-mh"</span><span class="w">

</span><span class="n">az</span><span class="w"> </span><span class="nx">role</span><span class="w"> </span><span class="nx">assignment</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"Key Vault Secrets Officer"</span><span class="w"> </span><span class="nt">--assignee</span><span class="w"> </span><span class="nv">$myObjectId</span><span class="w"> </span><span class="nt">--scope</span><span class="w"> </span><span class="nv">$scope</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">role</span><span class="w"> </span><span class="nx">assignment</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"Key Vault Secrets User"</span><span class="w">   </span><span class="nt">--assignee</span><span class="w"> </span><span class="nv">$myObjectId</span><span class="w"> </span><span class="nt">--scope</span><span class="w"> </span><span class="nv">$scope</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p>After this, <code class="language-plaintext highlighter-rouge">DefaultAzureCredential</code> on your local machine (authenticated via <code class="language-plaintext highlighter-rouge">az login</code>) will be able to read secrets, and you can manage secrets via the Portal.</p>
</blockquote>

<h2 id="step-4-add-secrets-to-key-vault">Step 4: Add Secrets to Key Vault</h2>

<blockquote>
  <p><strong>Naming convention:</strong> Azure Key Vault does not support <code class="language-plaintext highlighter-rouge">:</code> (colon) in secret names.<br />
The <code class="language-plaintext highlighter-rouge">Azure.Extensions.AspNetCore.Configuration.Secrets</code> package translates <code class="language-plaintext highlighter-rouge">--</code> (double dash) to <code class="language-plaintext highlighter-rouge">:</code> in <code class="language-plaintext highlighter-rouge">IConfiguration</code>.<br />
So <code class="language-plaintext highlighter-rouge">SqlServer--ConnectionString</code> in Key Vault becomes <code class="language-plaintext highlighter-rouge">SqlServer:ConnectionString</code> in your app.</p>
</blockquote>

<h3 id="step-by-step-for-each-secret">Step-by-step for each secret:</h3>

<ol>
  <li>In your Key Vault resource, click <strong>Objects → Secrets</strong> in the left menu.</li>
  <li>Click <strong>+ Generate/Import</strong>.</li>
  <li>Set:
    <ul>
      <li><strong>Upload options</strong> → <code class="language-plaintext highlighter-rouge">Manual</code></li>
      <li><strong>Name</strong> → <em>(see table below)</em></li>
      <li><strong>Secret value</strong> → <em>(see table below)</em></li>
    </ul>
  </li>
  <li>Click <strong>Create</strong>.</li>
</ol>

<p>Repeat for all 8 secrets:</p>

<table>
  <thead>
    <tr>
      <th>Secret Name (Key Vault)</th>
      <th>Secret Value</th>
      <th>Maps to (<code class="language-plaintext highlighter-rouge">IConfiguration</code> key)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SqlServer--ConnectionString</code></td>
      <td><code class="language-plaintext highlighter-rouge">Server=&lt;your-azure-sql-server&gt;.database.windows.net;Database=ECommerceDb;User Id=&lt;user&gt;;Password=&lt;pass&gt;;TrustServerCertificate=False;Encrypt=True;</code></td>
      <td><code class="language-plaintext highlighter-rouge">SqlServer:ConnectionString</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Jwt--SigningSecret</code></td>
      <td>A random string, <strong>minimum 32 characters</strong> (e.g., generate with <code class="language-plaintext highlighter-rouge">openssl rand -base64 32</code>)</td>
      <td><code class="language-plaintext highlighter-rouge">Jwt:SigningSecret</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Jwt--Issuer</code></td>
      <td><code class="language-plaintext highlighter-rouge">ECommerceApi</code></td>
      <td><code class="language-plaintext highlighter-rouge">Jwt:Issuer</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Jwt--Audience</code></td>
      <td><code class="language-plaintext highlighter-rouge">ECommerceApiUsers</code></td>
      <td><code class="language-plaintext highlighter-rouge">Jwt:Audience</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--Host</code></td>
      <td><code class="language-plaintext highlighter-rouge">smtp.sendgrid.net</code> <em>(or your SMTP provider)</em></td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:Host</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--Port</code></td>
      <td><code class="language-plaintext highlighter-rouge">587</code></td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:Port</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--Username</code></td>
      <td><code class="language-plaintext highlighter-rouge">apikey</code> <em>(SendGrid) or your username or email address</em></td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:Username</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--Password</code></td>
      <td>Your SMTP password / SendGrid API key</td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:Password</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--FromEmail</code></td>
      <td><code class="language-plaintext highlighter-rouge">noreply@yourdomain.com</code></td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:FromEmail</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Smtp--FromName</code></td>
      <td><code class="language-plaintext highlighter-rouge">ECommerce Store</code></td>
      <td><code class="language-plaintext highlighter-rouge">Smtp:FromName</code></td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><strong>Tip:</strong> For local development / testing SMTP, use <a href="https://mailtrap.io">Mailtrap</a> — free sandbox that catches all emails without sending them.</p>
</blockquote>

<p><img src="/assets/images/posts/2026/add-secret-key-akv.png" alt="" />
<strong>Fig : Adding a secret to Azure Key Vault</strong></p>

<h2 id="step-5-build-the-ecommerceapi-application-using-aspnet-core-web-api">Step 5: Build the ECommerceApi Application using asp.net core web api</h2>

<p>This section walks through creating the complete <code class="language-plaintext highlighter-rouge">ECommerceApi</code> project from scratch. Follow each step in order to build the same application.</p>

<h3 id="step-51--scaffold-the-project">Step 5.1 — Scaffold the Project</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mkdir</span><span class="w"> </span><span class="nx">src</span><span class="w">
</span><span class="n">cd</span><span class="w"> </span><span class="nx">src</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">new</span><span class="w"> </span><span class="nx">webapi</span><span class="w"> </span><span class="nt">-n</span><span class="w"> </span><span class="nx">ECommerceApi</span><span class="w">
</span><span class="n">cd</span><span class="w"> </span><span class="nx">ECommerceApi</span><span class="w">
</span></code></pre></div></div>

<p>Delete the generated <code class="language-plaintext highlighter-rouge">WeatherForecast.cs</code> and <code class="language-plaintext highlighter-rouge">Controllers/WeatherForecastController.cs</code> — they are not needed.</p>

<h3 id="step-52--add-nuget-packages">Step 5.2 — Add NuGet Packages</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Azure.Identity</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Azure.Extensions.AspNetCore.Configuration.Secrets</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">BCrypt.Net-Next</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">MailKit</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.AspNetCore.Authentication.JwtBearer</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.EntityFrameworkCore.SqlServer</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.EntityFrameworkCore.Tools</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.OpenApi</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Swashbuckle.AspNetCore</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-53--create-the-models">Step 5.3 — Create the Models</h3>

<p>Create a <code class="language-plaintext highlighter-rouge">Models/</code> folder and add the following four files.</p>

<p><strong><code class="language-plaintext highlighter-rouge">Models/User.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">User</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">PasswordHash</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Role</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">"Customer"</span><span class="p">;</span>
    <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>

    <span class="k">public</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;</span> <span class="n">Orders</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;();</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Models/Product.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Product</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>

    <span class="k">public</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;</span> <span class="n">OrderItems</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;();</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Models/Order.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">Order</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">UserId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">User</span> <span class="n">User</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">"Pending"</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalAmount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>

    <span class="k">public</span> <span class="n">ICollection</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;</span> <span class="n">Items</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;();</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Models/OrderItem.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">OrderItem</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">Order</span> <span class="n">Order</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">Product</span> <span class="n">Product</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">null</span><span class="p">!;</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-54--create-the-database-context">Step 5.4 — Create the Database Context</h3>

<p>Create <code class="language-plaintext highlighter-rouge">Data/AppDbContext.cs</code>:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Data</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">AppDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="nf">AppDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">AppDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>

    <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">User</span><span class="p">&gt;</span> <span class="n">Users</span> <span class="p">=&gt;</span> <span class="n">Set</span><span class="p">&lt;</span><span class="n">User</span><span class="p">&gt;();</span>
    <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="n">Products</span> <span class="p">=&gt;</span> <span class="n">Set</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;();</span>
    <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;</span> <span class="n">Orders</span> <span class="p">=&gt;</span> <span class="n">Set</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;();</span>
    <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;</span> <span class="n">OrderItems</span> <span class="p">=&gt;</span> <span class="n">Set</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;();</span>

    <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">User</span><span class="p">&gt;()</span>
            <span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">u</span> <span class="p">=&gt;</span> <span class="n">u</span><span class="p">.</span><span class="n">Email</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">IsUnique</span><span class="p">();</span>

        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;().</span><span class="nf">Property</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">).</span><span class="nf">HasPrecision</span><span class="p">(</span><span class="m">18</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;().</span><span class="nf">Property</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">).</span><span class="nf">HasPrecision</span><span class="p">(</span><span class="m">18</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;().</span><span class="nf">Property</span><span class="p">(</span><span class="n">oi</span> <span class="p">=&gt;</span> <span class="n">oi</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">).</span><span class="nf">HasPrecision</span><span class="p">(</span><span class="m">18</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>

        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;()</span>
            <span class="p">.</span><span class="nf">HasOne</span><span class="p">(</span><span class="n">oi</span> <span class="p">=&gt;</span> <span class="n">oi</span><span class="p">.</span><span class="n">Order</span><span class="p">).</span><span class="nf">WithMany</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Items</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">HasForeignKey</span><span class="p">(</span><span class="n">oi</span> <span class="p">=&gt;</span> <span class="n">oi</span><span class="p">.</span><span class="n">OrderId</span><span class="p">).</span><span class="nf">OnDelete</span><span class="p">(</span><span class="n">DeleteBehavior</span><span class="p">.</span><span class="n">Cascade</span><span class="p">);</span>

        <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">OrderItem</span><span class="p">&gt;()</span>
            <span class="p">.</span><span class="nf">HasOne</span><span class="p">(</span><span class="n">oi</span> <span class="p">=&gt;</span> <span class="n">oi</span><span class="p">.</span><span class="n">Product</span><span class="p">).</span><span class="nf">WithMany</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">OrderItems</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">HasForeignKey</span><span class="p">(</span><span class="n">oi</span> <span class="p">=&gt;</span> <span class="n">oi</span><span class="p">.</span><span class="n">ProductId</span><span class="p">).</span><span class="nf">OnDelete</span><span class="p">(</span><span class="n">DeleteBehavior</span><span class="p">.</span><span class="n">Restrict</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-55--create-the-dtos">Step 5.5 — Create the DTOs</h3>

<p>Create three subfolders: <code class="language-plaintext highlighter-rouge">DTOs/Auth/</code>, <code class="language-plaintext highlighter-rouge">DTOs/Products/</code>, <code class="language-plaintext highlighter-rouge">DTOs/Orders/</code>.</p>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Auth/RegisterDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Auth</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">RegisterDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">,</span> <span class="n">EmailAddress</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">,</span> <span class="nf">MinLength</span><span class="p">(</span><span class="m">6</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Password</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Auth/LoginDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Auth</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">LoginDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">,</span> <span class="n">EmailAddress</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Password</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Auth/AuthResponseDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Auth</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">AuthResponseDto</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Token</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Email</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Role</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Products/ProductDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Products</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">ProductDto</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Products/CreateProductDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Products</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">CreateProductDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0.01</span><span class="p">,</span> <span class="kt">double</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Orders/CreateOrderDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Orders</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">CreateOrderDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">,</span> <span class="nf">MinLength</span><span class="p">(</span><span class="m">1</span><span class="p">)]</span> <span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">OrderItemRequestDto</span><span class="p">&gt;</span> <span class="n">Items</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">OrderItemRequestDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Orders/OrderResponseDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Orders</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">OrderResponseDto</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalAmount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">OrderItemResponseDto</span><span class="p">&gt;</span> <span class="n">Items</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">OrderItemResponseDto</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">ProductName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Subtotal</span> <span class="p">=&gt;</span> <span class="n">Quantity</span> <span class="p">*</span> <span class="n">UnitPrice</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">DTOs/Orders/UpdateOrderStatusDto.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.DTOs.Orders</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">UpdateOrderStatusDto</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-56--create-the-services">Step 5.6 — Create the Services</h3>

<p><strong><code class="language-plaintext highlighter-rouge">Services/ITokenService.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>

<span class="k">public</span> <span class="k">interface</span> <span class="nc">ITokenService</span>
<span class="p">{</span>
    <span class="kt">string</span> <span class="nf">CreateToken</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Services/TokenService.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.IdentityModel.Tokens</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.IdentityModel.Tokens.Jwt</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Security.Claims</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">TokenService</span> <span class="p">:</span> <span class="n">ITokenService</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConfiguration</span> <span class="n">_config</span><span class="p">;</span>
    <span class="k">public</span> <span class="nf">TokenService</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">config</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_config</span> <span class="p">=</span> <span class="n">config</span><span class="p">;</span>

    <span class="k">public</span> <span class="kt">string</span> <span class="nf">CreateToken</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">secret</span> <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Jwt:SigningSecret"</span><span class="p">]</span>
            <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"JWT signing secret not configured."</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">key</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SymmetricSecurityKey</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="n">secret</span><span class="p">));</span>
        <span class="kt">var</span> <span class="n">creds</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SigningCredentials</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">SecurityAlgorithms</span><span class="p">.</span><span class="n">HmacSha256</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">claims</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
        <span class="p">{</span>
            <span class="k">new</span> <span class="nf">Claim</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">NameIdentifier</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Id</span><span class="p">.</span><span class="nf">ToString</span><span class="p">()),</span>
            <span class="k">new</span> <span class="nf">Claim</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">),</span>
            <span class="k">new</span> <span class="nf">Claim</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Name</span><span class="p">),</span>
            <span class="k">new</span> <span class="nf">Claim</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">Role</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Role</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="kt">var</span> <span class="n">token</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">JwtSecurityToken</span><span class="p">(</span>
            <span class="n">issuer</span><span class="p">:</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Jwt:Issuer"</span><span class="p">],</span>
            <span class="n">audience</span><span class="p">:</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Jwt:Audience"</span><span class="p">],</span>
            <span class="n">claims</span><span class="p">:</span> <span class="n">claims</span><span class="p">,</span>
            <span class="n">expires</span><span class="p">:</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">.</span><span class="nf">AddHours</span><span class="p">(</span><span class="m">24</span><span class="p">),</span>
            <span class="n">signingCredentials</span><span class="p">:</span> <span class="n">creds</span><span class="p">);</span>

        <span class="k">return</span> <span class="k">new</span> <span class="nf">JwtSecurityTokenHandler</span><span class="p">().</span><span class="nf">WriteToken</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Services/IEmailService.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>

<span class="k">public</span> <span class="k">interface</span> <span class="nc">IEmailService</span>
<span class="p">{</span>
    <span class="n">Task</span> <span class="nf">SendWelcomeEmailAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">toEmail</span><span class="p">,</span> <span class="kt">string</span> <span class="n">name</span><span class="p">);</span>
    <span class="n">Task</span> <span class="nf">SendOrderConfirmationAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">toEmail</span><span class="p">,</span> <span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">int</span> <span class="n">orderId</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">total</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Services/EmailService.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">MailKit.Net.Smtp</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MailKit.Security</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">MimeKit</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">EmailService</span> <span class="p">:</span> <span class="n">IEmailService</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConfiguration</span> <span class="n">_config</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">EmailService</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">EmailService</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">config</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">EmailService</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_config</span> <span class="p">=</span> <span class="n">config</span><span class="p">;</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">SendWelcomeEmailAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">toEmail</span><span class="p">,</span> <span class="kt">string</span> <span class="n">name</span><span class="p">)</span> <span class="p">=&gt;</span>
        <span class="k">await</span> <span class="nf">SendEmailAsync</span><span class="p">(</span><span class="n">toEmail</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="s">"Welcome to ECommerce Store!"</span><span class="p">,</span>
            <span class="s">$"&lt;h2&gt;Welcome, </span><span class="p">{</span><span class="n">name</span><span class="p">}</span><span class="s">!&lt;/h2&gt;&lt;p&gt;Thank you for registering at our store.&lt;/p&gt;"</span><span class="p">);</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">SendOrderConfirmationAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">toEmail</span><span class="p">,</span> <span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">int</span> <span class="n">orderId</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">total</span><span class="p">)</span> <span class="p">=&gt;</span>
        <span class="k">await</span> <span class="nf">SendEmailAsync</span><span class="p">(</span><span class="n">toEmail</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="s">$"Order #</span><span class="p">{</span><span class="n">orderId</span><span class="p">}</span><span class="s"> Confirmed"</span><span class="p">,</span>
            <span class="s">$"&lt;h2&gt;Order Confirmed, </span><span class="p">{</span><span class="n">name</span><span class="p">}</span><span class="s">!&lt;/h2&gt;&lt;p&gt;Order &lt;strong&gt;#</span><span class="p">{</span><span class="n">orderId</span><span class="p">}</span><span class="s">&lt;/strong&gt; — Total: &lt;strong&gt;$</span><span class="p">{</span><span class="n">total</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">&lt;/strong&gt;&lt;/p&gt;"</span><span class="p">);</span>

    <span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">SendEmailAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">toEmail</span><span class="p">,</span> <span class="kt">string</span> <span class="n">toName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">subject</span><span class="p">,</span> <span class="kt">string</span> <span class="n">htmlBody</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">host</span>     <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:Host"</span><span class="p">]</span>     <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"SMTP host not configured."</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">port</span>     <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:Port"</span><span class="p">]</span> <span class="p">??</span> <span class="s">"587"</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">username</span> <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:Username"</span><span class="p">]</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"SMTP username not configured."</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">password</span> <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:Password"</span><span class="p">]</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"SMTP password not configured."</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">fromEmail</span> <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:FromEmail"</span><span class="p">]</span> <span class="p">??</span> <span class="n">username</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">fromName</span>  <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="s">"Smtp:FromName"</span><span class="p">]</span>  <span class="p">??</span> <span class="s">"ECommerce Store"</span><span class="p">;</span>

        <span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MimeMessage</span><span class="p">();</span>
        <span class="n">message</span><span class="p">.</span><span class="n">From</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">MailboxAddress</span><span class="p">(</span><span class="n">fromName</span><span class="p">,</span> <span class="n">fromEmail</span><span class="p">));</span>
        <span class="n">message</span><span class="p">.</span><span class="n">To</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">MailboxAddress</span><span class="p">(</span><span class="n">toName</span><span class="p">,</span> <span class="n">toEmail</span><span class="p">));</span>
        <span class="n">message</span><span class="p">.</span><span class="n">Subject</span> <span class="p">=</span> <span class="n">subject</span><span class="p">;</span>
        <span class="n">message</span><span class="p">.</span><span class="n">Body</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TextPart</span><span class="p">(</span><span class="s">"html"</span><span class="p">)</span> <span class="p">{</span> <span class="n">Text</span> <span class="p">=</span> <span class="n">htmlBody</span> <span class="p">};</span>

        <span class="k">using</span> <span class="nn">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SmtpClient</span><span class="p">();</span>
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">ConnectAsync</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">SecureSocketOptions</span><span class="p">.</span><span class="n">StartTls</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">AuthenticateAsync</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">SendAsync</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">DisconnectAsync</span><span class="p">(</span><span class="k">true</span><span class="p">);</span>

        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Email sent to {Email} — Subject: {Subject}"</span><span class="p">,</span> <span class="n">toEmail</span><span class="p">,</span> <span class="n">subject</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-57--create-the-controllers">Step 5.7 — Create the Controllers</h3>

<p><strong><code class="language-plaintext highlighter-rouge">Controllers/AuthController.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.DTOs.Auth</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Controllers</span><span class="p">;</span>

<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">AuthController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">AppDbContext</span> <span class="n">_db</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ITokenService</span> <span class="n">_tokenService</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEmailService</span> <span class="n">_emailService</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">AuthController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">AuthController</span><span class="p">(</span><span class="n">AppDbContext</span> <span class="n">db</span><span class="p">,</span> <span class="n">ITokenService</span> <span class="n">tokenService</span><span class="p">,</span>
        <span class="n">IEmailService</span> <span class="n">emailService</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">AuthController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_db</span> <span class="p">=</span> <span class="n">db</span><span class="p">;</span> <span class="n">_tokenService</span> <span class="p">=</span> <span class="n">tokenService</span><span class="p">;</span>
        <span class="n">_emailService</span> <span class="p">=</span> <span class="n">emailService</span><span class="p">;</span> <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpPost</span><span class="p">(</span><span class="s">"register"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">AuthResponseDto</span><span class="p">&gt;&gt;</span> <span class="nf">Register</span><span class="p">(</span><span class="n">RegisterDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">AnyAsync</span><span class="p">(</span><span class="n">u</span> <span class="p">=&gt;</span> <span class="n">u</span><span class="p">.</span><span class="n">Email</span> <span class="p">==</span> <span class="n">dto</span><span class="p">.</span><span class="n">Email</span><span class="p">))</span>
            <span class="k">return</span> <span class="nf">Conflict</span><span class="p">(</span><span class="k">new</span> <span class="p">{</span> <span class="n">message</span> <span class="p">=</span> <span class="s">"Email is already registered."</span> <span class="p">});</span>

        <span class="kt">var</span> <span class="n">user</span> <span class="p">=</span> <span class="k">new</span> <span class="n">User</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
            <span class="n">Email</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span>
            <span class="n">PasswordHash</span> <span class="p">=</span> <span class="n">BCrypt</span><span class="p">.</span><span class="n">Net</span><span class="p">.</span><span class="n">BCrypt</span><span class="p">.</span><span class="nf">HashPassword</span><span class="p">(</span><span class="n">dto</span><span class="p">.</span><span class="n">Password</span><span class="p">)</span>
        <span class="p">};</span>
        <span class="n">_db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

        <span class="n">_</span> <span class="p">=</span> <span class="n">_emailService</span><span class="p">.</span><span class="nf">SendWelcomeEmailAsync</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Name</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ContinueWith</span><span class="p">(</span><span class="n">t</span> <span class="p">=&gt;</span> <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Exception</span><span class="p">,</span>
                <span class="s">"Welcome email failed for {Email}"</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">),</span>
                <span class="n">TaskContinuationOptions</span><span class="p">.</span><span class="n">OnlyOnFaulted</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">token</span> <span class="p">=</span> <span class="n">_tokenService</span><span class="p">.</span><span class="nf">CreateToken</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="k">new</span> <span class="n">AuthResponseDto</span> <span class="p">{</span> <span class="n">Token</span> <span class="p">=</span> <span class="n">token</span><span class="p">,</span> <span class="n">Email</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Role</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Role</span> <span class="p">});</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpPost</span><span class="p">(</span><span class="s">"login"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">AuthResponseDto</span><span class="p">&gt;&gt;</span> <span class="nf">Login</span><span class="p">(</span><span class="n">LoginDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">user</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">SingleOrDefaultAsync</span><span class="p">(</span><span class="n">u</span> <span class="p">=&gt;</span> <span class="n">u</span><span class="p">.</span><span class="n">Email</span> <span class="p">==</span> <span class="n">dto</span><span class="p">.</span><span class="n">Email</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">user</span> <span class="k">is</span> <span class="k">null</span> <span class="p">||</span> <span class="p">!</span><span class="n">BCrypt</span><span class="p">.</span><span class="n">Net</span><span class="p">.</span><span class="n">BCrypt</span><span class="p">.</span><span class="nf">Verify</span><span class="p">(</span><span class="n">dto</span><span class="p">.</span><span class="n">Password</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">PasswordHash</span><span class="p">))</span>
            <span class="k">return</span> <span class="nf">Unauthorized</span><span class="p">(</span><span class="k">new</span> <span class="p">{</span> <span class="n">message</span> <span class="p">=</span> <span class="s">"Invalid email or password."</span> <span class="p">});</span>

        <span class="kt">var</span> <span class="n">token</span> <span class="p">=</span> <span class="n">_tokenService</span><span class="p">.</span><span class="nf">CreateToken</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="k">new</span> <span class="n">AuthResponseDto</span> <span class="p">{</span> <span class="n">Token</span> <span class="p">=</span> <span class="n">token</span><span class="p">,</span> <span class="n">Email</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Role</span> <span class="p">=</span> <span class="n">user</span><span class="p">.</span><span class="n">Role</span> <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Controllers/ProductsController.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.DTOs.Products</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Controllers</span><span class="p">;</span>

<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ProductsController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">AppDbContext</span> <span class="n">_db</span><span class="p">;</span>
    <span class="k">public</span> <span class="nf">ProductsController</span><span class="p">(</span><span class="n">AppDbContext</span> <span class="n">db</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_db</span> <span class="p">=</span> <span class="n">db</span><span class="p">;</span>

    <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">ProductDto</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetAll</span><span class="p">()</span> <span class="p">=&gt;</span>
        <span class="nf">Ok</span><span class="p">(</span><span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">ProductDto</span>
        <span class="p">{</span>
            <span class="n">Id</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Description</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span>
            <span class="n">Price</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span> <span class="n">Stock</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Stock</span>
        <span class="p">}).</span><span class="nf">ToListAsync</span><span class="p">());</span>

    <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">ProductDto</span><span class="p">&gt;&gt;</span> <span class="nf">GetById</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">p</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">p</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="k">new</span> <span class="n">ProductDto</span> <span class="p">{</span> <span class="n">Id</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Description</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span> <span class="n">Price</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span> <span class="n">Stock</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Stock</span> <span class="p">});</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">ProductDto</span><span class="p">&gt;&gt;</span> <span class="nf">Create</span><span class="p">(</span><span class="n">CreateProductDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Product</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Description</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span> <span class="n">Price</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span> <span class="n">Stock</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Stock</span> <span class="p">};</span>
        <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
        <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductDto</span> <span class="p">{</span> <span class="n">Id</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">Description</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span> <span class="n">Price</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span> <span class="n">Stock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">};</span>
        <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetById</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="n">result</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">Update</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">CreateProductDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
        <span class="n">product</span><span class="p">.</span><span class="n">Name</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span> <span class="n">product</span><span class="p">.</span><span class="n">Description</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Description</span><span class="p">;</span>
        <span class="n">product</span><span class="p">.</span><span class="n">Price</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Price</span><span class="p">;</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Stock</span><span class="p">;</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpDelete</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">Delete</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
        <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Remove</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">Controllers/OrdersController.cs</code></strong></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">ECommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.DTOs.Orders</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Security.Claims</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Controllers</span><span class="p">;</span>

<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">OrdersController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">AppDbContext</span> <span class="n">_db</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEmailService</span> <span class="n">_emailService</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">OrdersController</span><span class="p">(</span><span class="n">AppDbContext</span> <span class="n">db</span><span class="p">,</span> <span class="n">IEmailService</span> <span class="n">emailService</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_db</span> <span class="p">=</span> <span class="n">db</span><span class="p">;</span> <span class="n">_emailService</span> <span class="p">=</span> <span class="n">emailService</span><span class="p">;</span> <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">OrderResponseDto</span><span class="p">&gt;&gt;</span> <span class="nf">CreateOrder</span><span class="p">(</span><span class="n">CreateOrderDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">userId</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="nf">FindFirstValue</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">NameIdentifier</span><span class="p">)!);</span>
        <span class="kt">var</span> <span class="n">user</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">userId</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">user</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">Unauthorized</span><span class="p">();</span>

        <span class="kt">var</span> <span class="n">productIds</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Items</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">i</span> <span class="p">=&gt;</span> <span class="n">i</span><span class="p">.</span><span class="n">ProductId</span><span class="p">).</span><span class="nf">Distinct</span><span class="p">().</span><span class="nf">ToList</span><span class="p">();</span>
        <span class="kt">var</span> <span class="n">products</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">productIds</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">)).</span><span class="nf">ToDictionaryAsync</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>

        <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Order</span> <span class="p">{</span> <span class="n">UserId</span> <span class="p">=</span> <span class="n">userId</span> <span class="p">};</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">dto</span><span class="p">.</span><span class="n">Items</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(!</span><span class="n">products</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">product</span><span class="p">))</span>
                <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">(</span><span class="k">new</span> <span class="p">{</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$"Product ID </span><span class="p">{</span><span class="n">item</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found."</span> <span class="p">});</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&lt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Quantity</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">(</span><span class="k">new</span> <span class="p">{</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$"Insufficient stock for '</span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">'."</span> <span class="p">});</span>

            <span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">-=</span> <span class="n">item</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
            <span class="n">order</span><span class="p">.</span><span class="n">Items</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">OrderItem</span> <span class="p">{</span> <span class="n">ProductId</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Quantity</span> <span class="p">=</span> <span class="n">item</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span> <span class="n">UnitPrice</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span> <span class="p">});</span>
            <span class="n">order</span><span class="p">.</span><span class="n">TotalAmount</span> <span class="p">+=</span> <span class="n">item</span><span class="p">.</span><span class="n">Quantity</span> <span class="p">*</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">_db</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

        <span class="n">_</span> <span class="p">=</span> <span class="n">_emailService</span><span class="p">.</span><span class="nf">SendOrderConfirmationAsync</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="n">Email</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">order</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ContinueWith</span><span class="p">(</span><span class="n">t</span> <span class="p">=&gt;</span> <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Exception</span><span class="p">,</span>
                <span class="s">"Order confirmation email failed for order #{OrderId}"</span><span class="p">,</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">),</span>
                <span class="n">TaskContinuationOptions</span><span class="p">.</span><span class="n">OnlyOnFaulted</span><span class="p">);</span>

        <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetById</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="nf">MapToDto</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">products</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">OrderResponseDto</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetMyOrders</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">userId</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="nf">FindFirstValue</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">NameIdentifier</span><span class="p">)!);</span>
        <span class="kt">var</span> <span class="n">orders</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Orders</span>
            <span class="p">.</span><span class="nf">Include</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Items</span><span class="p">).</span><span class="nf">ThenInclude</span><span class="p">(</span><span class="n">i</span> <span class="p">=&gt;</span> <span class="n">i</span><span class="p">.</span><span class="n">Product</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">UserId</span> <span class="p">==</span> <span class="n">userId</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">OrderByDescending</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">CreatedAt</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">orders</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="nf">MapToDto</span><span class="p">(</span><span class="n">o</span><span class="p">)));</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">OrderResponseDto</span><span class="p">&gt;&gt;</span> <span class="nf">GetById</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">userId</span> <span class="p">=</span> <span class="kt">int</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="nf">FindFirstValue</span><span class="p">(</span><span class="n">ClaimTypes</span><span class="p">.</span><span class="n">NameIdentifier</span><span class="p">)!);</span>
        <span class="kt">var</span> <span class="n">isAdmin</span> <span class="p">=</span> <span class="n">User</span><span class="p">.</span><span class="nf">IsInRole</span><span class="p">(</span><span class="s">"Admin"</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">Include</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Items</span><span class="p">).</span><span class="nf">ThenInclude</span><span class="p">(</span><span class="n">i</span> <span class="p">=&gt;</span> <span class="n">i</span><span class="p">.</span><span class="n">Product</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">FirstOrDefaultAsync</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">id</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">isAdmin</span> <span class="p">&amp;&amp;</span> <span class="n">order</span><span class="p">.</span><span class="n">UserId</span> <span class="p">!=</span> <span class="n">userId</span><span class="p">)</span> <span class="k">return</span> <span class="nf">Forbid</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="nf">MapToDto</span><span class="p">(</span><span class="n">order</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">HttpPatch</span><span class="p">(</span><span class="s">"{id}/status"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateStatus</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">UpdateOrderStatusDto</span> <span class="n">dto</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
        <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="n">dto</span><span class="p">.</span><span class="n">Status</span><span class="p">;</span>
        <span class="k">await</span> <span class="n">_db</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
        <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">static</span> <span class="n">OrderResponseDto</span> <span class="nf">MapToDto</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">,</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">Product</span><span class="p">&gt;?</span> <span class="n">productMap</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">=&gt;</span>
        <span class="k">new</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Id</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span> <span class="n">Status</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Status</span><span class="p">,</span>
            <span class="n">TotalAmount</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span> <span class="n">CreatedAt</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">CreatedAt</span><span class="p">,</span>
            <span class="n">Items</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Items</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">i</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">OrderItemResponseDto</span>
            <span class="p">{</span>
                <span class="n">ProductId</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                <span class="n">ProductName</span> <span class="p">=</span> <span class="n">productMap</span> <span class="p">!=</span> <span class="k">null</span>
                    <span class="p">?</span> <span class="p">(</span><span class="n">productMap</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">p</span><span class="p">)</span> <span class="p">?</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">)</span>
                    <span class="p">:</span> <span class="n">i</span><span class="p">.</span><span class="n">Product</span><span class="p">?.</span><span class="n">Name</span> <span class="p">??</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">,</span>
                <span class="n">Quantity</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span> <span class="n">UnitPrice</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="n">UnitPrice</span>
            <span class="p">}).</span><span class="nf">ToList</span><span class="p">()</span>
        <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="step-58---create-the-configcontroller-to-verify-that-secrets-are-loaded-correctly-from-azure-key-vault">Step 5.8 - Create the ConfigController to verify that secrets are loaded correctly from Azure Key Vault.</h3>
<blockquote>
  <p>This controller is for development/testing purposes only and should not be included in production.</p>
</blockquote>

<p><strong><code class="language-plaintext highlighter-rouge">Controllers/ConfigController.cs</code></strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ECommerceApi.Controllers</span><span class="p">;</span>

<span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Development-only endpoint to verify Azure Key Vault secrets are loaded correctly.</span>
<span class="c1">/// Remove or restrict this controller before deploying to production.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ConfigController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConfiguration</span> <span class="n">_config</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IWebHostEnvironment</span> <span class="n">_env</span><span class="p">;</span>

    <span class="c1">// Keys expected to be loaded from Azure Key Vault</span>
    <span class="k">private</span> <span class="k">static</span> <span class="k">readonly</span> <span class="kt">string</span><span class="p">[]</span> <span class="n">SecretKeys</span> <span class="p">=</span>
    <span class="p">[</span>
        <span class="s">"SqlServer:ConnectionString"</span><span class="p">,</span>
        <span class="s">"Jwt:SigningSecret"</span><span class="p">,</span>
        <span class="s">"Jwt:Issuer"</span><span class="p">,</span>
        <span class="s">"Jwt:Audience"</span><span class="p">,</span>
        <span class="s">"Smtp:Host"</span><span class="p">,</span>
        <span class="s">"Smtp:Port"</span><span class="p">,</span>
        <span class="s">"Smtp:Username"</span><span class="p">,</span>
        <span class="s">"Smtp:Password"</span><span class="p">,</span>
        <span class="s">"Smtp:FromEmail"</span><span class="p">,</span>
        <span class="s">"Smtp:FromName"</span>
    <span class="p">];</span>

    <span class="c1">// Keys whose values should be masked for safety</span>
    <span class="k">private</span> <span class="k">static</span> <span class="k">readonly</span> <span class="kt">string</span><span class="p">[]</span> <span class="n">SensitiveKeys</span> <span class="p">=</span>
    <span class="p">[</span>
        <span class="s">"SqlServer:ConnectionString"</span><span class="p">,</span>
        <span class="s">"Jwt:SigningSecret"</span><span class="p">,</span>
        <span class="s">"Smtp:Password"</span>
    <span class="p">];</span>

    <span class="k">public</span> <span class="nf">ConfigController</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">config</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_config</span> <span class="p">=</span> <span class="n">config</span><span class="p">;</span>
        <span class="n">_env</span> <span class="p">=</span> <span class="n">env</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Returns all Azure Key Vault secret keys and their values.</span>
    <span class="c1">/// Sensitive values (passwords, secrets) are partially masked.</span>
    <span class="c1">/// Only available in Development environment.</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"keyvault-secrets"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="n">IActionResult</span> <span class="nf">GetKeyVaultSecrets</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">_env</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
            <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

        <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">SecretKeys</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">key</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="k">value</span> <span class="p">=</span> <span class="n">_config</span><span class="p">[</span><span class="n">key</span><span class="p">];</span>
            <span class="kt">var</span> <span class="n">isSensitive</span> <span class="p">=</span> <span class="n">SensitiveKeys</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>

            <span class="k">return</span> <span class="k">new</span>
            <span class="p">{</span>
                <span class="n">Key</span> <span class="p">=</span> <span class="n">key</span><span class="p">,</span>
                <span class="n">Value</span> <span class="p">=</span> <span class="k">value</span> <span class="k">is</span> <span class="k">null</span>
                    <span class="p">?</span> <span class="s">"(not set)"</span>
                    <span class="p">:</span> <span class="n">isSensitive</span>
                        <span class="p">?</span> <span class="nf">MaskValue</span><span class="p">(</span><span class="k">value</span><span class="p">)</span>
                        <span class="p">:</span> <span class="k">value</span><span class="p">,</span>
                <span class="n">IsSet</span> <span class="p">=</span> <span class="k">value</span> <span class="k">is</span> <span class="k">not</span> <span class="k">null</span>
            <span class="p">};</span>
        <span class="p">});</span>

        <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">static</span> <span class="kt">string</span> <span class="nf">MaskValue</span><span class="p">(</span><span class="kt">string</span> <span class="k">value</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">value</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;=</span> <span class="m">6</span><span class="p">)</span>
            <span class="k">return</span> <span class="k">new</span> <span class="kt">string</span><span class="p">(</span><span class="sc">'*'</span><span class="p">,</span> <span class="k">value</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>

        <span class="k">return</span> <span class="k">value</span><span class="p">[..</span><span class="m">3</span><span class="p">]</span> <span class="p">+</span> <span class="k">new</span> <span class="kt">string</span><span class="p">(</span><span class="sc">'*'</span><span class="p">,</span> <span class="k">value</span><span class="p">.</span><span class="n">Length</span> <span class="p">-</span> <span class="m">6</span><span class="p">)</span> <span class="p">+</span> <span class="k">value</span><span class="p">[^</span><span class="m">3</span><span class="p">..];</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="step-59--configure-appsettingsjson-to-include-the-key-vault-uri-and-load-secrets-at-runtime">Step 5.9 — Configure appsettings.json to include the Key Vault URI and load secrets at runtime.</h3>

<p>Open <code class="language-plaintext highlighter-rouge">appsettings.json</code> and replace its contents with:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"KeyVaultUri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://kv-ecommerce-dev-mh.vault.azure.net/"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-510--configure-programcs">Step 5.10 — Configure Program.cs</h3>

<p>Replace the generated <code class="language-plaintext highlighter-rouge">Program.cs</code> with the following code. Here keyVaultUri is read from <code class="language-plaintext highlighter-rouge">appsettings.json</code> and the <code class="language-plaintext highlighter-rouge">Azure.Extensions.AspNetCore.Configuration.Secrets</code> package is used to load all secrets from Key Vault into <code class="language-plaintext highlighter-rouge">IConfiguration</code> at startup.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Azure.Identity</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ECommerceApi.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Authentication.JwtBearer</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.IdentityModel.Tokens</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.OpenApi</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Azure Key Vault — loads all secrets into IConfiguration at startup</span>
<span class="kt">var</span> <span class="n">keyVaultUri</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">[</span><span class="s">"KeyVaultUri"</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">keyVaultUri</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">AddAzureKeyVault</span><span class="p">(</span>
        <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="n">keyVaultUri</span><span class="p">),</span>
        <span class="k">new</span> <span class="nf">DefaultAzureCredential</span><span class="p">());</span>
<span class="p">}</span>

<span class="c1">// Database (connection string comes from Key Vault secret: SqlServer--ConnectionString)</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">AppDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span>
        <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">[</span><span class="s">"SqlServer:ConnectionString"</span><span class="p">]</span>
            <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"SqlServer:ConnectionString is not configured."</span><span class="p">)));</span>

<span class="c1">// JWT Authentication (signing secret comes from Key Vault secret: Jwt--SigningSecret)</span>
<span class="kt">var</span> <span class="n">jwtSecret</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">[</span><span class="s">"Jwt:SigningSecret"</span><span class="p">]</span>
    <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="s">"Jwt:SigningSecret is not configured."</span><span class="p">);</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddAuthentication</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddJwtBearer</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">options</span><span class="p">.</span><span class="n">TokenValidationParameters</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TokenValidationParameters</span>
        <span class="p">{</span>
            <span class="n">ValidateIssuer</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
            <span class="n">ValidateAudience</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
            <span class="n">ValidateLifetime</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
            <span class="n">ValidateIssuerSigningKey</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
            <span class="n">ValidIssuer</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">[</span><span class="s">"Jwt:Issuer"</span><span class="p">],</span>
            <span class="n">ValidAudience</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">[</span><span class="s">"Jwt:Audience"</span><span class="p">],</span>
            <span class="n">IssuerSigningKey</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SymmetricSecurityKey</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="n">jwtSecret</span><span class="p">))</span>
        <span class="p">};</span>
    <span class="p">});</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddAuthorization</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">ITokenService</span><span class="p">,</span> <span class="n">TokenService</span><span class="p">&gt;();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IEmailService</span><span class="p">,</span> <span class="n">EmailService</span><span class="p">&gt;();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerDoc</span><span class="p">(</span><span class="s">"v1"</span><span class="p">,</span> <span class="k">new</span> <span class="n">OpenApiInfo</span> <span class="p">{</span> <span class="n">Title</span> <span class="p">=</span> <span class="s">"ECommerce API"</span><span class="p">,</span> <span class="n">Version</span> <span class="p">=</span> <span class="s">"v1"</span> <span class="p">});</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityDefinition</span><span class="p">(</span><span class="s">"Bearer"</span><span class="p">,</span> <span class="k">new</span> <span class="n">OpenApiSecurityScheme</span>
    <span class="p">{</span>
        <span class="n">Name</span> <span class="p">=</span> <span class="s">"Authorization"</span><span class="p">,</span>
        <span class="n">Type</span> <span class="p">=</span> <span class="n">SecuritySchemeType</span><span class="p">.</span><span class="n">ApiKey</span><span class="p">,</span>
        <span class="n">In</span> <span class="p">=</span> <span class="n">ParameterLocation</span><span class="p">.</span><span class="n">Header</span><span class="p">,</span>
        <span class="n">Description</span> <span class="p">=</span> <span class="s">"Enter: Bearer {your token}"</span>
    <span class="p">});</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityRequirement</span><span class="p">(</span><span class="n">_</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">OpenApiSecurityRequirement</span>
    <span class="p">{</span>
        <span class="p">{</span> <span class="k">new</span> <span class="nf">OpenApiSecuritySchemeReference</span><span class="p">(</span><span class="s">"Bearer"</span><span class="p">),</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()</span> <span class="p">}</span>
    <span class="p">});</span>
<span class="p">});</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerEndpoint</span><span class="p">(</span><span class="s">"/swagger/v1/swagger.json"</span><span class="p">,</span> <span class="s">"ECommerce API v1"</span><span class="p">));</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="c1">// Seed a default admin user on first run</span>
<span class="c1">// Credentials: admin@ecommerce.com / Admin1234!</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">db</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">AppDbContext</span><span class="p">&gt;();</span>
    <span class="n">db</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(!</span><span class="n">db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">Any</span><span class="p">(</span><span class="n">u</span> <span class="p">=&gt;</span> <span class="n">u</span><span class="p">.</span><span class="n">Email</span> <span class="p">==</span> <span class="s">"admin@ecommerce.com"</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">db</span><span class="p">.</span><span class="n">Users</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">ECommerceApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">User</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"Admin"</span><span class="p">,</span>
            <span class="n">Email</span> <span class="p">=</span> <span class="s">"admin@ecommerce.com"</span><span class="p">,</span>
            <span class="n">PasswordHash</span> <span class="p">=</span> <span class="n">BCrypt</span><span class="p">.</span><span class="n">Net</span><span class="p">.</span><span class="n">BCrypt</span><span class="p">.</span><span class="nf">HashPassword</span><span class="p">(</span><span class="s">"Admin1234!"</span><span class="p">),</span>
            <span class="n">Role</span> <span class="p">=</span> <span class="s">"Admin"</span><span class="p">,</span>
            <span class="n">CreatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span>
        <span class="p">});</span>
        <span class="n">db</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>

<h3 id="step-511--verify-the-build">Step 5.11 — Verify the Build</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dotnet</span><span class="w"> </span><span class="nx">build</span><span class="w">
</span></code></pre></div></div>

<p>Expected output: <code class="language-plaintext highlighter-rouge">Build succeeded</code> with 0 errors.</p>

<hr />
<h2 id="step-6--create--apply-entity-framework-migrations">Step 6 — Create &amp; Apply Entity Framework Migrations</h2>

<p>Ensure EF Core CLI tools are installed:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dotnet</span><span class="w"> </span><span class="nx">tool</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--global</span><span class="w"> </span><span class="nx">dotnet-ef</span><span class="w">
</span></code></pre></div></div>

<p>Navigate to the project directory:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cd</span><span class="w"> </span><span class="nx">src/ECommerceApi</span><span class="w">
</span></code></pre></div></div>

<p>Create the initial migration:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dotnet</span><span class="w"> </span><span class="nx">ef</span><span class="w"> </span><span class="nx">migrations</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">InitialCreate</span><span class="w">
</span></code></pre></div></div>

<p>Apply the migration to your database:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dotnet</span><span class="w"> </span><span class="nx">ef</span><span class="w"> </span><span class="nx">database</span><span class="w"> </span><span class="nx">update</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p>For production, run <code class="language-plaintext highlighter-rouge">dotnet ef database update</code> during your CI/CD pipeline or apply the SQL script generated by <code class="language-plaintext highlighter-rouge">dotnet ef migrations script</code>.</p>
</blockquote>

<hr />

<h2 id="step-7--run-the-application-locally">Step 7 — Run the Application Locally</h2>

<h3 id="step-71--authenticate-azure-cli-if-using-key-vault-locally">Step 7.1 — Authenticate Azure CLI (if using Key Vault locally)</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">az</span><span class="w"> </span><span class="nx">login</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">account</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nt">--subscription</span><span class="w"> </span><span class="s2">"&lt;your-subscription-id&gt;"</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-72--run-the-application">Step 7.2 — Run the application</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cd</span><span class="w"> </span><span class="nx">src/ECommerceApi</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">run</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-73--open-swagger-ui">Step 7.3 — Open Swagger UI</h3>

<p>Navigate to the URL shown in the terminal (typically):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://localhost:5001/swagger
</code></pre></div></div>
<blockquote>
  <p>Port may vary based on your launch settings. Look for the HTTPS URL in the terminal output.</p>
</blockquote>

<p>Now you can test all API endpoints via Swagger UI. The <code class="language-plaintext highlighter-rouge">ConfigController</code> endpoint (<code class="language-plaintext highlighter-rouge">GET /api/config/keyvault-secrets</code>) will show you which secrets are loaded from Azure Key Vault and their masked values. You can also test user registration, login, product management, and order creation to verify that everything is working end-to-end with secrets securely loaded from Key Vault.</p>

<h2 id="notes">Notes</h2>
<h3 id="deploy-application-to-azure-app-service">Deploy Application to Azure App Service</h3>
<p>When deploying to Azure App Service, ensure that the App Service’s managed identity has <code class="language-plaintext highlighter-rouge">Secret Reader</code> access to the Azure Key Vault. The application will automatically load secrets from Key Vault at startup using the same code, so no changes are needed in your application configuration for production deployment. Just make sure to set the <code class="language-plaintext highlighter-rouge">KeyVaultUri</code> in <code class="language-plaintext highlighter-rouge">appsettings.json</code> to point to your production Key Vault.</p>

<h3 id="how-to-connect-to-azure-key-vault-in-different-environments-quick-reference">How to connect to Azure Key Vault in different Environments (Quick Reference)</h3>

<table>
  <thead>
    <tr>
      <th>Environment</th>
      <th>Auth Method</th>
      <th>What to set up</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Local (your machine)</strong></td>
      <td>Azure CLI session</td>
      <td><code class="language-plaintext highlighter-rouge">az login</code> + assign <strong>Key Vault Secrets User</strong> role to your Azure AD account</td>
    </tr>
    <tr>
      <td><strong>Another developer’s machine</strong></td>
      <td>Azure CLI session</td>
      <td>They run <code class="language-plaintext highlighter-rouge">az login</code> + a team admin assigns the same role to their account</td>
    </tr>
    <tr>
      <td><strong>Azure App Service</strong></td>
      <td>Managed Identity</td>
      <td>Enable System-assigned MI on the App Service + assign <strong>Key Vault Secrets User</strong> role to it</td>
    </tr>
    <tr>
      <td><strong>IIS / on-premises server</strong></td>
      <td>Service Principal (env vars)</td>
      <td>Create a Service Principal, set 3 env vars on the server, assign role to the SP</td>
    </tr>
    <tr>
      <td><strong>CI/CD pipeline</strong></td>
      <td>Service Principal (env vars)</td>
      <td>Same SP, set vars as pipeline secrets</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>
<p>In this tutorial, you built a complete ASP.NET Core Web API application with secure integration to Azure Key Vault for managing sensitive configuration like database connection strings, JWT secrets, and SMTP credentials. You created a simple e-commerce API with user authentication, product management, and order processing features. The application securely loads all secrets from Azure Key Vault at runtime, ensuring that sensitive information is never hardcoded or stored in source control.</p>

<p>If you have any questions or need further assistance, feel free to comment in the comments section below. If you found this tutorial helpful, please share in facebook, linkedin, twitter or any other social media platform.
Happy coding!</p>

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/main/asp-azure-key-vault">Complete Source Code on GitHub</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term="ASP.NET Core" /><category term="Azure" /><category term=".NET" /><category term="Web API" /><category term="Configuration" /><category term="Security" /><category term="aspnetcore" /><category term="azure" /><category term="dotnet" /><category term="csharp" /><category term="webapi" /><category term="configuration" /><category term="devops" /><category term="auth" /><category term="security" /><category term="keyvault" /><summary type="html"><![CDATA[This step-by-step tutorial demonstrates how to integrate Azure Key Vault with an ASP.NET Core Web API application. You will learn how to create a Key Vault, store secrets, configure access, and use secrets in your application.]]></summary></entry><entry><title type="html">How to Build a Sample Microservices Application Using ASP.NET Core, Apache Kafka, and Docker</title><link href="https://mahedee.net/how-to-build-microservices-aspnet-core-apache-kafka-docker/" rel="alternate" type="text/html" title="How to Build a Sample Microservices Application Using ASP.NET Core, Apache Kafka, and Docker" /><published>2026-03-01T00:00:00-05:00</published><updated>2026-03-01T00:00:00-05:00</updated><id>https://mahedee.net/how-to-build-microservices-aspnet-core-apache-kafka-docker</id><content type="html" xml:base="https://mahedee.net/how-to-build-microservices-aspnet-core-apache-kafka-docker/"><![CDATA[<p>Microservices architecture has become increasingly popular for building scalable, maintainable applications. In this comprehensive tutorial, we’ll build a sample e-commerce application with two microservices using ASP.NET Core, Apache Kafka for event-driven communication, and Docker for containerization.</p>

<h2 id="what-well-build">What We’ll Build</h2>

<p>We’ll create a simple e-commerce system with two microservices:</p>
<ul>
  <li><strong>Product Service</strong>: Manages product catalog</li>
  <li><strong>Order Service</strong>: Handles order processing</li>
</ul>

<p>These services will communicate through Apache Kafka events, each having its own SQL Server database. When a product is created, the Product Service will publish a <code class="language-plaintext highlighter-rouge">ProductCreated</code> event. The Order Service will subscribe to this event to update its local cache or perform other actions. On the other hand, when an order is created, the Order Service will publish an <code class="language-plaintext highlighter-rouge">OrderCreated</code> event and the Product Service can listen to it for inventory updates.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>Before we start, make sure you have:</p>
<ul>
  <li>.NET 10 SDK</li>
  <li>Docker Desktop</li>
  <li>Visual Studio 2026 or Visual Studio Code</li>
  <li>Basic knowledge of ASP.NET Core, Docker, and microservices concepts</li>
</ul>

<h2 id="project-structure">Project Structure</h2>

<p>Let’s start by creating our solution structure:</p>

<p><img src="/assets/images/posts/2026/solution_structure.png" alt="" /></p>

<h2 id="step-1-create-the-solution-and-projects">Step 1: Create the Solution and Projects</h2>

<p>Open a terminal and create the solution structure:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>MicroservicesDemo
<span class="nb">cd </span>MicroservicesDemo

<span class="c"># Create solution file</span>
dotnet new sln

<span class="c"># Create directories</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> src/Services/ProductService
<span class="nb">mkdir</span> <span class="nt">-p</span> src/Services/OrderService
<span class="nb">mkdir</span> <span class="nt">-p</span> src/Shared/EventBus

<span class="c"># Create projects</span>
<span class="nb">cd </span>src/Services/ProductService
dotnet new webapi <span class="nt">-n</span> ProductService

<span class="nb">cd</span> ../OrderService
dotnet new webapi <span class="nt">-n</span> OrderService

<span class="nb">cd</span> ../../Shared/EventBus
dotnet new classlib <span class="nt">-n</span> EventBus

<span class="c"># Add projects to solution</span>
<span class="nb">cd</span> ../../../
dotnet sln add .<span class="se">\s</span>rc<span class="se">\S</span>ervices<span class="se">\P</span>roductService<span class="se">\P</span>roductService<span class="se">\P</span>roductService.csproj
  
dotnet sln add .<span class="se">\s</span>rc<span class="se">\S</span>ervices<span class="se">\O</span>rderService<span class="se">\O</span>rderService<span class="se">\O</span>rderService.csproj
dotnet sln add .<span class="se">\s</span>rc<span class="se">\S</span>hared<span class="se">\E</span>ventBus<span class="se">\E</span>ventBus<span class="se">\E</span>ventBus.csproj
</code></pre></div></div>

<h2 id="step-2-create-shared-event-bus-library">Step 2: Create Shared Event Bus Library</h2>

<p>First, let’s create our shared event bus library for Kafka integration. The purpose of this library is to provide a common interface and implementation for publishing and subscribing to events using Apache Kafka.</p>

<h3 id="create-event-bus-interface">Create Event Bus Interface</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">IEventBus</code> interface in the <code class="language-plaintext highlighter-rouge">EventBus</code> project. The interface will define methods for publishing and subscribing to events. 
<strong>EventBus/IEventBus.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EventBus</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IEventBus</span>
    <span class="p">{</span>
        <span class="n">Task</span> <span class="n">PublishAsync</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">T</span> <span class="n">@event</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">class</span><span class="err">;</span>
        <span class="nc">Task</span> <span class="n">SubscribeAsync</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">Func</span><span class="p">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Task</span><span class="p">&gt;</span> <span class="n">handler</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">class</span><span class="err">;</span>
    <span class="err">}</span>
<span class="err">}</span>
</code></pre></div></div>

<h3 id="create-base-event-class">Create Base Event Class</h3>
<p>Create a base event class name <code class="language-plaintext highlighter-rouge">BaseEvent</code> that other events will inherit from. This abstract class has properties for event ID and creation timestamp which are common to all events.</p>

<p><strong>EventBus/Events/BaseEvent.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EventBus.Events</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">abstract</span> <span class="k">class</span> <span class="nc">BaseEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="n">Guid</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">protected</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">protected</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="nf">BaseEvent</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Id</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="nf">NewGuid</span><span class="p">();</span>
            <span class="n">CreatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-productcreatedevent">Create ProductCreatedEvent</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductCreatedEvent</code> class that represents the event published when a new product is created.</p>

<p><strong>EventBus/Events/ProductCreatedEvent.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">EventBus.Events</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductCreatedEvent</span> <span class="p">:</span> <span class="n">BaseEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-ordercreatedevent">Create OrderCreatedEvent</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">OrderCreatedEvent</code> class that represents the event published when a new order is created.</p>

<p><strong>EventBus/Events/OrderCreatedEvent.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">EventBus.Events</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderCreatedEvent</span> <span class="p">:</span> <span class="n">BaseEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-productupdatedevent">Create ProductUpdatedEvent</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductUpdatedEvent</code> class that represents the event published when a product is updated.</p>

<p><strong>EventBus/Events/ProductUpdatedEvent.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">EventBus.Events</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductUpdatedEvent</span> <span class="p">:</span> <span class="n">BaseEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">PreviousPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">PreviousStock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="create-kafkaeventbus-implementation">Create KafkaEventBus Implementation</h3>
<p>Now, let’s implement the <code class="language-plaintext highlighter-rouge">KafkaEventBus</code> class that uses Confluent.Kafka to publish and subscribe to events. In this implementation, we will handle event serialization/deserialization, topic management, and error handling. KafkaEventBus implements the IEventBus interface using Apache Kafka as the underlying message broker, featuring core properties like _producer for publishing events, _consumer for receiving messages, _handlers dictionary to store event type-specific callback functions, and _subscribedTopics list to track active subscriptions. The PublishAsync<T> method serializes events to JSON and sends them to dynamically named Kafka topics (e.g., "microservices.ProductCreatedEvent"), while SubscribeAsync<T> registers event handlers and manages topic subscriptions, automatically starting a background consumer task via StartConsumerAsync() that continuously polls Kafka topics for incoming messages. This architecture enables loose coupling between microservices by allowing them to communicate asynchronously through events rather than direct API calls - for instance, when ProductService creates a product, it publishes a ProductCreatedEvent that OrderService automatically receives and processes to update its local product cache, ensuring real-time data synchronization across the distributed system without services needing to know about each other's existence or endpoints.</T></T></p>

<h3 id="eventbuskafkaeventbuscs">EventBus/KafkaEventBus.cs</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Confluent.Kafka</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EventBus</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">KafkaEventBus</span> <span class="p">:</span> <span class="n">IEventBus</span><span class="p">,</span> <span class="n">IDisposable</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IProducer</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="n">_producer</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConsumer</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="n">_consumer</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">KafkaEventBus</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="kt">string</span> <span class="n">_topicPrefix</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="n">Func</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="n">Task</span><span class="p">&gt;&gt;</span> <span class="n">_handlers</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">_subscribedTopics</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
        <span class="k">private</span> <span class="kt">bool</span> <span class="n">_isConsuming</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">KafkaEventBus</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">KafkaEventBus</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
            <span class="n">_topicPrefix</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"Kafka:TopicPrefix"</span><span class="p">]</span> <span class="p">??</span> <span class="s">"microservices"</span><span class="p">;</span>

            <span class="kt">var</span> <span class="n">producerConfig</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProducerConfig</span>
            <span class="p">{</span>
                <span class="n">BootstrapServers</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"Kafka:BootstrapServers"</span><span class="p">]</span>
            <span class="p">};</span>

            <span class="kt">var</span> <span class="n">consumerConfig</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ConsumerConfig</span>
            <span class="p">{</span>
                <span class="n">BootstrapServers</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"Kafka:BootstrapServers"</span><span class="p">],</span>
                <span class="n">GroupId</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s">"Kafka:GroupId"</span><span class="p">],</span>
                <span class="n">AutoOffsetReset</span> <span class="p">=</span> <span class="n">AutoOffsetReset</span><span class="p">.</span><span class="n">Earliest</span>
            <span class="p">};</span>

            <span class="n">_producer</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProducerBuilder</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;(</span><span class="n">producerConfig</span><span class="p">).</span><span class="nf">Build</span><span class="p">();</span>
            <span class="n">_consumer</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ConsumerBuilder</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;(</span><span class="n">consumerConfig</span><span class="p">).</span><span class="nf">Build</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="n">PublishAsync</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">T</span> <span class="n">@event</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">class</span>
        <span class="err">{</span>
            <span class="nc">var</span> <span class="n">topic</span> <span class="p">=</span> <span class="s">$"</span><span class="p">{</span><span class="n">_topicPrefix</span><span class="p">}</span><span class="s">.</span><span class="p">{</span><span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
            <span class="kt">var</span> <span class="n">message</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">@event</span><span class="p">);</span>

            <span class="k">try</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_producer</span><span class="p">.</span><span class="nf">ProduceAsync</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="k">new</span> <span class="n">Message</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span>
                <span class="p">{</span>
                    <span class="n">Key</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="nf">NewGuid</span><span class="p">().</span><span class="nf">ToString</span><span class="p">(),</span>
                    <span class="n">Value</span> <span class="p">=</span> <span class="n">message</span>
                <span class="p">});</span>

                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Event published to topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">: </span><span class="p">{</span><span class="n">result</span><span class="p">.</span><span class="n">Status</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Failed to publish event to topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="n">SubscribeAsync</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">Func</span><span class="p">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Task</span><span class="p">&gt;</span> <span class="n">handler</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="k">class</span>
        <span class="err">{</span>
            <span class="nc">var</span> <span class="n">topic</span> <span class="p">=</span> <span class="s">$"</span><span class="p">{</span><span class="n">_topicPrefix</span><span class="p">}</span><span class="s">.</span><span class="p">{</span><span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Subscribing to topic: </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Store the handler for this topic</span>
                <span class="n">_handlers</span><span class="p">[</span><span class="n">topic</span><span class="p">]</span> <span class="p">=</span> <span class="k">async</span> <span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">@event</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>
                    <span class="k">await</span> <span class="nf">handler</span><span class="p">(</span><span class="n">@event</span><span class="p">);</span>
                <span class="p">};</span>

                <span class="n">_subscribedTopics</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">topic</span><span class="p">);</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Added topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s"> to subscription list"</span><span class="p">);</span>

                <span class="c1">// Start the consumer if not already started</span>
                <span class="k">if</span> <span class="p">(!</span><span class="n">_isConsuming</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="k">await</span> <span class="nf">StartConsumerAsync</span><span class="p">();</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="c1">// Re-subscribe to all topics</span>
                    <span class="n">_consumer</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">_subscribedTopics</span><span class="p">);</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Re-subscribed to all topics: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">_subscribedTopics</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Successfully subscribed to topic: </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Failed to subscribe to topic: </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">StartConsumerAsync</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">_isConsuming</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
            <span class="n">_consumer</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">_subscribedTopics</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Subscribed to topics: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">_subscribedTopics</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span>

            <span class="n">_</span> <span class="p">=</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Starting consumer task for topics: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">_subscribedTopics</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span>
                <span class="k">while</span> <span class="p">(</span><span class="k">true</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="k">try</span>
                    <span class="p">{</span>
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span><span class="s">$"Polling for messages on topics: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">_subscribedTopics</span><span class="p">)}</span><span class="s">..."</span><span class="p">);</span>
                        <span class="kt">var</span> <span class="n">consumeResult</span> <span class="p">=</span> <span class="n">_consumer</span><span class="p">.</span><span class="nf">Consume</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromMilliseconds</span><span class="p">(</span><span class="m">1000</span><span class="p">));</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">consumeResult</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                        <span class="p">{</span>
                            <span class="kt">var</span> <span class="n">topic</span> <span class="p">=</span> <span class="n">consumeResult</span><span class="p">.</span><span class="n">Topic</span><span class="p">;</span>
                            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received message from topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">: </span><span class="p">{</span><span class="n">consumeResult</span><span class="p">.</span><span class="n">Message</span><span class="p">.</span><span class="n">Value</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                            
                            <span class="k">if</span> <span class="p">(</span><span class="n">_handlers</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">handler</span><span class="p">))</span>
                            <span class="p">{</span>
                                <span class="k">await</span> <span class="nf">handler</span><span class="p">(</span><span class="n">consumeResult</span><span class="p">.</span><span class="n">Message</span><span class="p">.</span><span class="n">Value</span><span class="p">);</span>
                                <span class="n">_consumer</span><span class="p">.</span><span class="nf">Commit</span><span class="p">(</span><span class="n">consumeResult</span><span class="p">);</span>
                                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Successfully processed message from topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                            <span class="p">}</span>
                            <span class="k">else</span>
                            <span class="p">{</span>
                                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"No handler found for topic </span><span class="p">{</span><span class="n">topic</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                            <span class="p">}</span>
                        <span class="p">}</span>
                        <span class="k">else</span>
                        <span class="p">{</span>
                            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span><span class="s">$"No message received from any topic, continuing polling..."</span><span class="p">);</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                    <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
                    <span class="p">{</span>
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Error consuming message from topics"</span><span class="p">);</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">});</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">void</span> <span class="nf">Dispose</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">_producer</span><span class="p">?.</span><span class="nf">Dispose</span><span class="p">();</span>
            <span class="n">_consumer</span><span class="p">?.</span><span class="nf">Dispose</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="update-eventbuscsproj">Update EventBus.csproj</h3>

<p>Finally, update the <code class="language-plaintext highlighter-rouge">EventBus.csproj</code> file to include the necessary NuGet packages for Kafka integration and logging.</p>

<p><strong>EventBus/EventBus.csproj</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk"</span><span class="nt">&gt;</span>

  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;TargetFramework&gt;</span>net8.0<span class="nt">&lt;/TargetFramework&gt;</span>
    <span class="nt">&lt;ImplicitUsings&gt;</span>enable<span class="nt">&lt;/ImplicitUsings&gt;</span>
    <span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>

  <span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Confluent.Kafka"</span> <span class="na">Version=</span><span class="s">"2.3.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.Extensions.Configuration.Abstractions"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.Extensions.Logging.Abstractions"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/ItemGroup&gt;</span>

<span class="nt">&lt;/Project&gt;</span>

</code></pre></div></div>

<h2 id="step-3-build-product-service">Step 3: Build Product Service</h2>

<h3 id="create-product-model">Create Product Model</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">Product</code> model class that represents the product entity in the Product Service.</p>

<p><strong>ProductService/Models/Product.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ProductService.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Product</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">500</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0.01</span><span class="p">,</span> <span class="kt">double</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-productcontext">Create ProductContext</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductContext</code> class that represents the Entity Framework Core database context for the Product Service.</p>

<p><strong>ProductService/Data/ProductContext.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ProductService.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">ProductContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">ProductContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="n">Products</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;(</span><span class="n">entity</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasKey</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Name</span><span class="p">).</span><span class="nf">IsRequired</span><span class="p">().</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Description</span><span class="p">).</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">500</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Price</span><span class="p">).</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"decimal(18,2)"</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
            <span class="p">});</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-ordereventhandler">Create OrderEventHandler</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">OrderEventHandler</code> class that will handle events from the Order Service. OrderEventHandler is called automatically when an order is placed in the OrderService through the event-driven architecture. It is configured in Program.cs file.</p>

<p><strong>ProductService/Services/OrderEventHandler.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ProductService.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderEventHandler</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrderEventHandler</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">OrderEventHandler</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrderEventHandler</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">,</span> <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">OrderCreatedEvent</span> <span class="n">@event</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order created event received: Order </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">, Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s">, Quantity </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Quantity</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            
            <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
            <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ProductContext</span><span class="p">&gt;();</span>
            <span class="kt">var</span> <span class="n">eventBus</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">IEventBus</span><span class="p">&gt;();</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Find the product</span>
                <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Update product stock (reduce by order quantity)</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&gt;=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Quantity</span><span class="p">)</span>
                    <span class="p">{</span>
                        <span class="kt">var</span> <span class="n">originalStock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">;</span>
                        <span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">-=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
                        <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
                        
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> stock updated. Stock: </span><span class="p">{</span><span class="n">originalStock</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                        
                        <span class="c1">// Publish ProductUpdatedEvent to notify other services about stock change</span>
                        <span class="kt">var</span> <span class="n">productUpdatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductUpdatedEvent</span>
                        <span class="p">{</span>
                            <span class="n">ProductId</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                            <span class="n">Name</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                            <span class="n">Description</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span>
                            <span class="n">Price</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                            <span class="n">Stock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                            <span class="n">PreviousStock</span> <span class="p">=</span> <span class="n">originalStock</span><span class="p">,</span>
                            <span class="n">PreviousPrice</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span>  <span class="c1">// Price didn't change</span>
                        <span class="p">};</span>

                        <span class="k">await</span> <span class="n">eventBus</span><span class="p">.</span><span class="nf">PublishAsync</span><span class="p">(</span><span class="n">productUpdatedEvent</span><span class="p">);</span>
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"ProductUpdatedEvent published for Product </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> due to order </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                        
                        <span class="c1">// Check if stock is low</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&lt;=</span> <span class="m">10</span><span class="p">)</span>
                        <span class="p">{</span>
                            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"LOW STOCK ALERT: Product </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s"> (ID: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s">) has only </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s"> units remaining!"</span><span class="p">);</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                    <span class="k">else</span>
                    <span class="p">{</span>
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Insufficient stock for Product </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s">. Available: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s">, Requested: </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Quantity</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Product with ID </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found when processing order </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Error processing order created event for Order </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-productscontroller">Create ProductsController</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductsController</code> class that exposes RESTful APIs for managing products in the Product Service.</p>

<p><strong>ProductService/Controllers/ProductsController.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ProductService.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductsController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ProductContext</span> <span class="n">_context</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEventBus</span> <span class="n">_eventBus</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductsController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">ProductsController</span><span class="p">(</span><span class="n">ProductContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">IEventBus</span> <span class="n">eventBus</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductsController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
            <span class="n">_eventBus</span> <span class="p">=</span> <span class="n">eventBus</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetProducts</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">GetProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="k">return</span> <span class="n">product</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">CreateProduct</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

            <span class="c1">// Publish event</span>
            <span class="kt">var</span> <span class="n">productCreatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductCreatedEvent</span>
            <span class="p">{</span>
                <span class="n">ProductId</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                <span class="n">Name</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                <span class="n">Price</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                <span class="n">Stock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span>
            <span class="p">};</span>

            <span class="k">await</span> <span class="n">_eventBus</span><span class="p">.</span><span class="nf">PublishAsync</span><span class="p">(</span><span class="n">productCreatedEvent</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product created and event published: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

            <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetProduct</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="n">product</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">id</span> <span class="p">!=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">();</span>

            <span class="c1">// Get the original product to track changes</span>
            <span class="kt">var</span> <span class="n">originalProduct</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">AsNoTracking</span><span class="p">().</span><span class="nf">FirstOrDefaultAsync</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">originalProduct</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="n">_context</span><span class="p">.</span><span class="nf">Entry</span><span class="p">(</span><span class="n">product</span><span class="p">).</span><span class="n">State</span> <span class="p">=</span> <span class="n">EntityState</span><span class="p">.</span><span class="n">Modified</span><span class="p">;</span>

            <span class="k">try</span>
            <span class="p">{</span>
                <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
                
                <span class="c1">// Publish product updated event</span>
                <span class="kt">var</span> <span class="n">productUpdatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductUpdatedEvent</span>
                <span class="p">{</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                    <span class="n">Name</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                    <span class="n">Description</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span>
                    <span class="n">Price</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                    <span class="n">Stock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                    <span class="n">PreviousPrice</span> <span class="p">=</span> <span class="n">originalProduct</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                    <span class="n">PreviousStock</span> <span class="p">=</span> <span class="n">originalProduct</span><span class="p">.</span><span class="n">Stock</span>
                <span class="p">};</span>

                <span class="k">await</span> <span class="n">_eventBus</span><span class="p">.</span><span class="nf">PublishAsync</span><span class="p">(</span><span class="n">productUpdatedEvent</span><span class="p">);</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product updated and event published: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">. Price: $</span><span class="p">{</span><span class="n">originalProduct</span><span class="p">.</span><span class="n">Price</span><span class="p">}</span><span class="s"> → $</span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">}</span><span class="s">, Stock: </span><span class="p">{</span><span class="n">originalProduct</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">DbUpdateConcurrencyException</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">if</span> <span class="p">(!</span><span class="nf">ProductExists</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
                    <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpDelete</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">DeleteProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Remove</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

            <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"low-stock"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetLowStockProducts</span><span class="p">([</span><span class="n">FromQuery</span><span class="p">]</span> <span class="kt">int</span> <span class="n">threshold</span> <span class="p">=</span> <span class="m">10</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">lowStockProducts</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span>
                <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&lt;=</span> <span class="n">threshold</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="k">new</span> 
                <span class="p">{</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                    <span class="n">Status</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">Stock</span> <span class="p">==</span> <span class="m">0</span> <span class="p">?</span> <span class="s">"Out of Stock"</span> <span class="p">:</span> <span class="s">"Low Stock"</span>
                <span class="p">})</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

            <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">lowStockProducts</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}/stock"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateStock</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="p">[</span><span class="n">FromBody</span><span class="p">]</span> <span class="n">UpdateStockRequest</span> <span class="n">request</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="kt">var</span> <span class="n">originalStock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">;</span>
            <span class="n">product</span><span class="p">.</span><span class="n">Stock</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">NewStock</span><span class="p">;</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

            <span class="c1">// Publish ProductUpdatedEvent for stock changes</span>
            <span class="kt">var</span> <span class="n">productUpdatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductUpdatedEvent</span>
            <span class="p">{</span>
                <span class="n">ProductId</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                <span class="n">Name</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                <span class="n">Description</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Description</span><span class="p">,</span>
                <span class="n">Price</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                <span class="n">Stock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                <span class="n">PreviousStock</span> <span class="p">=</span> <span class="n">originalStock</span><span class="p">,</span>
                <span class="n">PreviousPrice</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">Price</span>  <span class="c1">// Price didn't change, so previous = current</span>
            <span class="p">};</span>

            <span class="k">await</span> <span class="n">_eventBus</span><span class="p">.</span><span class="nf">PublishAsync</span><span class="p">(</span><span class="n">productUpdatedEvent</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Stock updated for product </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">: </span><span class="p">{</span><span class="n">originalStock</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">request</span><span class="p">.</span><span class="n">NewStock</span><span class="p">}</span><span class="s"> and event published"</span><span class="p">);</span>
            
            <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProductExists</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Any</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">id</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">record</span> <span class="nc">UpdateStockRequest</span><span class="p">(</span><span class="kt">int</span> <span class="n">NewStock</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="modify-programcs">Modify Program.cs</h3>
<p>Modify the <code class="language-plaintext highlighter-rouge">Program.cs</code> as follows to set up the services, database context, and event subscriptions.</p>

<p><strong>ProductService/Program.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">ProductService.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services to the container</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerDoc</span><span class="p">(</span><span class="s">"v1"</span><span class="p">,</span> <span class="k">new</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">OpenApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">OpenApiInfo</span>
    <span class="p">{</span>
        <span class="n">Title</span> <span class="p">=</span> <span class="s">"Product Service API"</span><span class="p">,</span>
        <span class="n">Version</span> <span class="p">=</span> <span class="s">"v1"</span><span class="p">,</span>
        <span class="n">Description</span> <span class="p">=</span> <span class="s">"API for managing products in the microservices demo"</span><span class="p">,</span>
        <span class="n">Contact</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">OpenApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">OpenApiContact</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"Product Service Team"</span><span class="p">,</span>
            <span class="n">Email</span> <span class="p">=</span> <span class="s">"products@microservicesdemo.com"</span>
        <span class="p">}</span>
    <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// Add Entity Framework</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">ProductContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

<span class="c1">// Add EventBus</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IEventBus</span><span class="p">,</span> <span class="n">KafkaEventBus</span><span class="p">&gt;();</span>

<span class="c1">// Add Event Handlers</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">OrderEventHandler</span><span class="p">&gt;();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Configure the HTTP request pipeline</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerEndpoint</span><span class="p">(</span><span class="s">"/swagger/v1/swagger.json"</span><span class="p">,</span> <span class="s">"Product Service API v1"</span><span class="p">);</span>
        <span class="n">c</span><span class="p">.</span><span class="n">RoutePrefix</span> <span class="p">=</span> <span class="s">"swagger"</span><span class="p">;</span>
        <span class="n">c</span><span class="p">.</span><span class="n">DocumentTitle</span> <span class="p">=</span> <span class="s">"Product Service API Documentation"</span><span class="p">;</span>
    <span class="p">});</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="c1">// Ensure database is created with retry logic</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ProductContext</span><span class="p">&gt;();</span>
    <span class="kt">var</span> <span class="n">logger</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Program</span><span class="p">&gt;&gt;();</span>
    
    <span class="kt">var</span> <span class="n">retryCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">maxRetries</span> <span class="p">=</span> <span class="m">30</span><span class="p">;</span>
    
    <span class="k">while</span> <span class="p">(</span><span class="n">retryCount</span> <span class="p">&lt;</span> <span class="n">maxRetries</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Attempting to connect to database, attempt {Attempt}"</span><span class="p">,</span> <span class="n">retryCount</span> <span class="p">+</span> <span class="m">1</span><span class="p">);</span>
            <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Database connection successful!"</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">retryCount</span><span class="p">++;</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">"Database connection failed (attempt {Attempt}/{MaxAttempts}): {Error}"</span><span class="p">,</span> <span class="n">retryCount</span><span class="p">,</span> <span class="n">maxRetries</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
            
            <span class="k">if</span> <span class="p">(</span><span class="n">retryCount</span> <span class="p">&gt;=</span> <span class="n">maxRetries</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to connect to database after {MaxAttempts} attempts. Exiting."</span><span class="p">,</span> <span class="n">maxRetries</span><span class="p">);</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
            
            <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">2000</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Subscribe to events</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">eventBus</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">IEventBus</span><span class="p">&gt;();</span>
    <span class="kt">var</span> <span class="n">orderEventHandler</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderEventHandler</span><span class="p">&gt;();</span>
    <span class="kt">var</span> <span class="n">logger</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Program</span><span class="p">&gt;&gt;();</span>
    
    <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Subscribing to OrderCreatedEvent..."</span><span class="p">);</span>
    <span class="k">await</span> <span class="n">eventBus</span><span class="p">.</span><span class="n">SubscribeAsync</span><span class="p">&lt;</span><span class="n">OrderCreatedEvent</span><span class="p">&gt;(</span><span class="n">orderEventHandler</span><span class="p">.</span><span class="n">Handle</span><span class="p">);</span>
    <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Successfully subscribed to OrderCreatedEvent"</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>

</code></pre></div></div>

<h3 id="modify-appsettingsjson">Modify appsettings.json</h3>
<p>Modify the <code class="language-plaintext highlighter-rouge">appsettings.json</code> as follows to include the database connection string and Kafka configuration.</p>

<p><strong>ProductService/appsettings.json</strong></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Server=sqlserver,1433;Database=ProductsDB;User Id=sa;Password=YourPassword123!;TrustServerCertificate=True;"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Kafka"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"BootstrapServers"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kafka:9092"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TopicPrefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ecommerce"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"GroupId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"product-service"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="update-productservicecsproj">Update ProductService.csproj</h3>
<p>Update the <code class="language-plaintext highlighter-rouge">ProductService.csproj</code> file to include the necessary NuGet packages for Entity Framework Core, Swagger, and reference the EventBus project. It also references the shared EventBus project to enable event-driven communication.</p>

<p><strong>ProductService/ProductService.csproj</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk.Web"</span><span class="nt">&gt;</span>

  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;TargetFramework&gt;</span>net8.0<span class="nt">&lt;/TargetFramework&gt;</span>
    <span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span>
    <span class="nt">&lt;ImplicitUsings&gt;</span>enable<span class="nt">&lt;/ImplicitUsings&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>

  <span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.AspNetCore.OpenApi"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Swashbuckle.AspNetCore"</span> <span class="na">Version=</span><span class="s">"6.5.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore.SqlServer"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore.Design"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/ItemGroup&gt;</span>

  <span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;ProjectReference</span> <span class="na">Include=</span><span class="s">"..\..\..\Shared\EventBus\EventBus\EventBus.csproj"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/ItemGroup&gt;</span>

<span class="nt">&lt;/Project&gt;</span>

</code></pre></div></div>

<h2 id="step-4-build-order-service">Step 4: Build Order Service</h2>

<h3 id="create-order-model">Create Order Model</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">Order</code> model class that represents the order entity in the Order Service.</p>

<p><strong>OrderService/Models/Order.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OrderService.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Order</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">ProductName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0.01</span><span class="p">,</span> <span class="kt">double</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">Range</span><span class="p">(</span><span class="m">0.01</span><span class="p">,</span> <span class="kt">double</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="n">EmailAddress</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="n">OrderStatus</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">Pending</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">enum</span> <span class="n">OrderStatus</span>
    <span class="p">{</span>
        <span class="n">Pending</span><span class="p">,</span>
        <span class="n">Confirmed</span><span class="p">,</span>
        <span class="n">Cancelled</span><span class="p">,</span>
        <span class="n">Shipped</span><span class="p">,</span>
        <span class="n">Delivered</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-productcache-model">Create ProductCache Model</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductCache</code> model class that represents a cached product in the Order Service.</p>

<p><strong>OrderService/Models/ProductCache.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OrderService.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductCache</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">500</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Stock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">LastUpdated</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="kt">bool</span> <span class="n">IsAvailable</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-ordercontext">Create OrderContext</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">OrderContext</code> class that represents the Entity Framework Core database context for the Order Service.</p>

<p><strong>OrderService/Data/OrderContext.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OrderService.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">OrderContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">OrderContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;</span> <span class="n">Orders</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">ProductCache</span><span class="p">&gt;</span> <span class="n">ProductCache</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;(</span><span class="n">entity</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasKey</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">ProductName</span><span class="p">).</span><span class="nf">IsRequired</span><span class="p">().</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">CustomerEmail</span><span class="p">).</span><span class="nf">IsRequired</span><span class="p">().</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">).</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"decimal(18,2)"</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">TotalPrice</span><span class="p">).</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"decimal(18,2)"</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Status</span><span class="p">).</span><span class="n">HasConversion</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">CustomerEmail</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Status</span><span class="p">);</span>
            <span class="p">});</span>

            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">ProductCache</span><span class="p">&gt;(</span><span class="n">entity</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasKey</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Name</span><span class="p">).</span><span class="nf">IsRequired</span><span class="p">().</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Description</span><span class="p">).</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">500</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Price</span><span class="p">).</span><span class="nf">HasColumnType</span><span class="p">(</span><span class="s">"decimal(18,2)"</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
            <span class="p">});</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="create-producteventhandler">Create ProductEventHandler</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">ProductEventHandler</code> class that will handle events from the Product Service. ProductEventHandler is called automatically when a product is created or updated in the ProductService through the event-driven architecture. It is configured in Program.cs file.</p>

<p><strong>OrderService/Services/ProductEventHandler.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OrderService.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductEventHandler</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductEventHandler</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">ProductEventHandler</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductEventHandler</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">,</span> <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">ProductCreatedEvent</span> <span class="n">@event</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product created event received: </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> - </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            
            <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
            <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderContext</span><span class="p">&gt;();</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Cache the product information locally for faster order processing</span>
                <span class="kt">var</span> <span class="n">cachedProduct</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ProductCache</span>
                <span class="p">{</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                    <span class="n">Name</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">""</span><span class="p">,</span> <span class="c1">// ProductCreatedEvent doesn't include description</span>
                    <span class="n">Price</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                    <span class="n">Stock</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                    <span class="n">IsAvailable</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">,</span>
                    <span class="n">LastUpdated</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span>
                <span class="p">};</span>

                <span class="n">context</span><span class="p">.</span><span class="n">ProductCache</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">cachedProduct</span><span class="p">);</span>
                <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
                
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> cached locally. Stock: </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s">, Available: </span><span class="p">{</span><span class="n">cachedProduct</span><span class="p">.</span><span class="n">IsAvailable</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Error caching product created event for Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">ProductUpdatedEvent</span> <span class="n">@event</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product updated event received: </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> - </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Name</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            
            <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
            <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderContext</span><span class="p">&gt;();</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Update cached product information</span>
                <span class="kt">var</span> <span class="n">cachedProduct</span> <span class="p">=</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">ProductCache</span>
                    <span class="p">.</span><span class="nf">FirstOrDefaultAsync</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">ProductId</span> <span class="p">==</span> <span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">cachedProduct</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">Name</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">Description</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Description</span><span class="p">;</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">Price</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Price</span><span class="p">;</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">Stock</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Stock</span><span class="p">;</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">IsAvailable</span> <span class="p">=</span> <span class="n">@event</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">;</span>
                    <span class="n">cachedProduct</span><span class="p">.</span><span class="n">LastUpdated</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>

                    <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
                    
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> cache updated. Stock: </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s">, Available: </span><span class="p">{</span><span class="n">cachedProduct</span><span class="p">.</span><span class="n">IsAvailable</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

                    <span class="c1">// Check for price changes and log important business events</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">Price</span> <span class="p">!=</span> <span class="n">@event</span><span class="p">.</span><span class="n">PreviousPrice</span><span class="p">)</span>
                    <span class="p">{</span>
                        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"PRICE CHANGE: Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> price changed from $</span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">PreviousPrice</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s"> to $</span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Price</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                        
                        <span class="c1">// Check pending orders with old price</span>
                        <span class="k">await</span> <span class="nf">CheckPendingOrdersForPriceChanges</span><span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span> <span class="n">@event</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span> <span class="n">@event</span><span class="p">.</span><span class="n">PreviousPrice</span><span class="p">,</span> <span class="n">context</span><span class="p">);</span>
                    <span class="p">}</span>

                    <span class="c1">// Check for stock changes</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">Stock</span> <span class="p">!=</span> <span class="n">@event</span><span class="p">.</span><span class="n">PreviousStock</span><span class="p">)</span>
                    <span class="p">{</span>
                        <span class="k">if</span> <span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">Stock</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
                        <span class="p">{</span>
                            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"OUT OF STOCK: Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> is now out of stock"</span><span class="p">);</span>
                            <span class="k">await</span> <span class="nf">CheckPendingOrdersForOutOfStock</span><span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span> <span class="n">context</span><span class="p">);</span>
                        <span class="p">}</span>
                        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">@event</span><span class="p">.</span><span class="n">PreviousStock</span> <span class="p">==</span> <span class="m">0</span> <span class="p">&amp;&amp;</span> <span class="n">@event</span><span class="p">.</span><span class="n">Stock</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
                        <span class="p">{</span>
                            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"BACK IN STOCK: Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> is now available with </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">Stock</span><span class="p">}</span><span class="s"> units"</span><span class="p">);</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found in cache when processing update event"</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">$"Error processing product updated event for Product </span><span class="p">{</span><span class="n">@event</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">CheckPendingOrdersForPriceChanges</span><span class="p">(</span><span class="kt">int</span> <span class="n">productId</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">newPrice</span><span class="p">,</span> <span class="kt">decimal</span> <span class="n">oldPrice</span><span class="p">,</span> <span class="n">OrderContext</span> <span class="n">context</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Find pending orders for this product</span>
            <span class="kt">var</span> <span class="n">pendingOrders</span> <span class="p">=</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">Orders</span>
                <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">ProductId</span> <span class="p">==</span> <span class="n">productId</span> <span class="p">&amp;&amp;</span> <span class="n">o</span><span class="p">.</span><span class="n">Status</span> <span class="p">==</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">Pending</span> <span class="p">&amp;&amp;</span> <span class="n">o</span><span class="p">.</span><span class="n">UnitPrice</span> <span class="p">==</span> <span class="n">oldPrice</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">pendingOrders</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Found </span><span class="p">{</span><span class="n">pendingOrders</span><span class="p">.</span><span class="n">Count</span><span class="p">}</span><span class="s"> pending orders for Product </span><span class="p">{</span><span class="n">productId</span><span class="p">}</span><span class="s"> with old price $</span><span class="p">{</span><span class="n">oldPrice</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">. New price: $</span><span class="p">{</span><span class="n">newPrice</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                
                <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">order</span> <span class="k">in</span> <span class="n">pendingOrders</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> may need price adjustment: ordered at $</span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">, current price $</span><span class="p">{</span><span class="n">newPrice</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">CheckPendingOrdersForOutOfStock</span><span class="p">(</span><span class="kt">int</span> <span class="n">productId</span><span class="p">,</span> <span class="n">OrderContext</span> <span class="n">context</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Find pending orders for out-of-stock products</span>
            <span class="kt">var</span> <span class="n">pendingOrders</span> <span class="p">=</span> <span class="k">await</span> <span class="n">context</span><span class="p">.</span><span class="n">Orders</span>
                <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">ProductId</span> <span class="p">==</span> <span class="n">productId</span> <span class="p">&amp;&amp;</span> <span class="n">o</span><span class="p">.</span><span class="n">Status</span> <span class="p">==</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">Pending</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">pendingOrders</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"ALERT: Found </span><span class="p">{</span><span class="n">pendingOrders</span><span class="p">.</span><span class="n">Count</span><span class="p">}</span><span class="s"> pending orders for out-of-stock Product </span><span class="p">{</span><span class="n">productId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                
                <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">order</span> <span class="k">in</span> <span class="n">pendingOrders</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> for Product </span><span class="p">{</span><span class="n">productId</span><span class="p">}</span><span class="s"> may need to be cancelled - product out of stock"</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="create-orderscontroller">Create OrdersController</h3>
<p>Create the <code class="language-plaintext highlighter-rouge">OrdersController</code> class that exposes RESTful APIs for managing orders in the Order Service.</p>

<p><strong>OrderService/Controllers/OrdersController.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OrderService.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrdersController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">OrderContext</span> <span class="n">_context</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEventBus</span> <span class="n">_eventBus</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">OrdersController</span><span class="p">(</span><span class="n">OrderContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">IEventBus</span> <span class="n">eventBus</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
            <span class="n">_eventBus</span> <span class="p">=</span> <span class="n">eventBus</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetOrders</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;&gt;</span> <span class="nf">GetOrder</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="k">return</span> <span class="n">order</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;&gt;</span> <span class="nf">CreateOrder</span><span class="p">(</span><span class="n">CreateOrderRequest</span> <span class="n">request</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Order</span>
            <span class="p">{</span>
                <span class="n">ProductId</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                <span class="n">ProductName</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">ProductName</span><span class="p">,</span>
                <span class="n">Quantity</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span>
                <span class="n">UnitPrice</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">,</span>
                <span class="n">TotalPrice</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">Quantity</span> <span class="p">*</span> <span class="n">request</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">,</span>
                <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">CustomerEmail</span>
            <span class="p">};</span>

            <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

            <span class="c1">// Publish event</span>
            <span class="kt">var</span> <span class="n">orderCreatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OrderCreatedEvent</span>
            <span class="p">{</span>
                <span class="n">OrderId</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                <span class="n">ProductId</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                <span class="n">Quantity</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span>
                <span class="n">TotalPrice</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">TotalPrice</span><span class="p">,</span>
                <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">CustomerEmail</span>
            <span class="p">};</span>

            <span class="k">await</span> <span class="n">_eventBus</span><span class="p">.</span><span class="nf">PublishAsync</span><span class="p">(</span><span class="n">orderCreatedEvent</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order created and event published: </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

            <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetOrder</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="n">order</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}/status"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateOrderStatus</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">UpdateOrderStatusRequest</span> <span class="n">request</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>

            <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">Status</span><span class="p">;</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

            <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"product-cache"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetCachedProducts</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">cachedProducts</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">ProductCache</span>
                <span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="k">new</span>
                <span class="p">{</span>
                    <span class="n">Id</span> <span class="p">=</span> <span class="n">p</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>  <span class="c1">// Return the actual ProductId from ProductService</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">Stock</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">IsAvailable</span><span class="p">,</span>
                    <span class="n">p</span><span class="p">.</span><span class="n">LastUpdated</span>
                <span class="p">})</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

            <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">cachedProducts</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"pending-by-product/{productId}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Order</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetPendingOrdersByProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">productId</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">orders</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span>
                <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">ProductId</span> <span class="p">==</span> <span class="n">productId</span> <span class="p">&amp;&amp;</span> <span class="n">o</span><span class="p">.</span><span class="n">Status</span> <span class="p">==</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">Pending</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>

            <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">orders</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">record</span> <span class="nc">CreateOrderRequest</span><span class="p">(</span>
        <span class="kt">int</span> <span class="n">ProductId</span><span class="p">,</span>
        <span class="kt">string</span> <span class="n">ProductName</span><span class="p">,</span>
        <span class="kt">int</span> <span class="n">Quantity</span><span class="p">,</span>
        <span class="kt">decimal</span> <span class="n">UnitPrice</span><span class="p">,</span>
        <span class="kt">string</span> <span class="n">CustomerEmail</span><span class="p">);</span>

    <span class="k">public</span> <span class="k">record</span> <span class="nc">UpdateOrderStatusRequest</span><span class="p">(</span><span class="n">OrderStatus</span> <span class="n">Status</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="modify-programcs-1">Modify Program.cs</h3>
<p>Modify the <code class="language-plaintext highlighter-rouge">Program.cs</code> as follows to set up the services, database context, and event subscriptions.</p>

<p><strong>OrderService/Program.cs</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">OrderService.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EventBus.Events</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services to the container</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerDoc</span><span class="p">(</span><span class="s">"v1"</span><span class="p">,</span> <span class="k">new</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">OpenApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">OpenApiInfo</span>
    <span class="p">{</span>
        <span class="n">Title</span> <span class="p">=</span> <span class="s">"Order Service API"</span><span class="p">,</span>
        <span class="n">Version</span> <span class="p">=</span> <span class="s">"v1"</span><span class="p">,</span>
        <span class="n">Description</span> <span class="p">=</span> <span class="s">"API for managing orders in the microservices demo"</span><span class="p">,</span>
        <span class="n">Contact</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">OpenApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">OpenApiContact</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"Order Service Team"</span><span class="p">,</span>
            <span class="n">Email</span> <span class="p">=</span> <span class="s">"orders@microservicesdemo.com"</span>
        <span class="p">}</span>
    <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// Add Entity Framework</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">OrderContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

<span class="c1">// Add EventBus and handlers</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IEventBus</span><span class="p">,</span> <span class="n">KafkaEventBus</span><span class="p">&gt;();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ProductEventHandler</span><span class="p">&gt;();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Configure the HTTP request pipeline</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerEndpoint</span><span class="p">(</span><span class="s">"/swagger/v1/swagger.json"</span><span class="p">,</span> <span class="s">"Order Service API v1"</span><span class="p">);</span>
        <span class="n">c</span><span class="p">.</span><span class="n">RoutePrefix</span> <span class="p">=</span> <span class="s">"swagger"</span><span class="p">;</span>
        <span class="n">c</span><span class="p">.</span><span class="n">DocumentTitle</span> <span class="p">=</span> <span class="s">"Order Service API Documentation"</span><span class="p">;</span>
    <span class="p">});</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="c1">// Ensure database is created with retry logic</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderContext</span><span class="p">&gt;();</span>
    <span class="kt">var</span> <span class="n">logger</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Program</span><span class="p">&gt;&gt;();</span>
    
    <span class="kt">var</span> <span class="n">retryCount</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">maxRetries</span> <span class="p">=</span> <span class="m">30</span><span class="p">;</span>
    
    <span class="k">while</span> <span class="p">(</span><span class="n">retryCount</span> <span class="p">&lt;</span> <span class="n">maxRetries</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Attempting to connect to database, attempt {Attempt}"</span><span class="p">,</span> <span class="n">retryCount</span> <span class="p">+</span> <span class="m">1</span><span class="p">);</span>
            
            <span class="c1">// For demo purposes, we'll recreate the database to ensure schema is current</span>
            <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureDeleted</span><span class="p">();</span>
            <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Database connection successful!"</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">retryCount</span><span class="p">++;</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">"Database connection failed (attempt {Attempt}/{MaxAttempts}): {Error}"</span><span class="p">,</span> <span class="n">retryCount</span><span class="p">,</span> <span class="n">maxRetries</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
            
            <span class="k">if</span> <span class="p">(</span><span class="n">retryCount</span> <span class="p">&gt;=</span> <span class="n">maxRetries</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to connect to database after {MaxAttempts} attempts. Exiting."</span><span class="p">,</span> <span class="n">maxRetries</span><span class="p">);</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
            
            <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">2000</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Subscribe to events</span>
<span class="k">try</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">eventBus</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">IEventBus</span><span class="p">&gt;();</span>
    <span class="kt">var</span> <span class="n">productEventHandler</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ProductEventHandler</span><span class="p">&gt;();</span>
    
    <span class="n">app</span><span class="p">.</span><span class="n">Logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Setting up event subscriptions..."</span><span class="p">);</span>
    
    <span class="k">await</span> <span class="n">eventBus</span><span class="p">.</span><span class="n">SubscribeAsync</span><span class="p">&lt;</span><span class="n">ProductCreatedEvent</span><span class="p">&gt;(</span><span class="n">productEventHandler</span><span class="p">.</span><span class="n">Handle</span><span class="p">);</span>
    <span class="n">app</span><span class="p">.</span><span class="n">Logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Subscribed to ProductCreatedEvent"</span><span class="p">);</span>
    
    <span class="k">await</span> <span class="n">eventBus</span><span class="p">.</span><span class="n">SubscribeAsync</span><span class="p">&lt;</span><span class="n">ProductUpdatedEvent</span><span class="p">&gt;(</span><span class="n">productEventHandler</span><span class="p">.</span><span class="n">Handle</span><span class="p">);</span>
    <span class="n">app</span><span class="p">.</span><span class="n">Logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Subscribed to ProductUpdatedEvent"</span><span class="p">);</span>
    
    <span class="n">app</span><span class="p">.</span><span class="n">Logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Event subscriptions setup completed"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="n">Logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Failed to setup event subscriptions"</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>

</code></pre></div></div>
<h3 id="modify-appsettingsjson-1">Modify appsettings.json</h3>
<p>Modify the <code class="language-plaintext highlighter-rouge">appsettings.json</code> as follows to include the database connection string and Kafka configuration.</p>

<p><strong>OrderService/appsettings.json</strong></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Server=sqlserver,1433;Database=OrdersDB;User Id=sa;Password=YourPassword123!;TrustServerCertificate=True;"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Kafka"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"BootstrapServers"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kafka:9092"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TopicPrefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ecommerce"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"GroupId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"order-service"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Debug"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"EventBus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Debug"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="modify-orderservicecsproj">Modify OrderService.csproj</h3>
<p>Modify the <code class="language-plaintext highlighter-rouge">OrderService.csproj</code> file to include the necessary NuGet packages for Entity Framework Core, Swagger, and reference the EventBus project. It also references the shared EventBus project to enable event-driven communication.</p>

<p><strong>OrderService/OrderService.csproj</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk.Web"</span><span class="nt">&gt;</span>

  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;TargetFramework&gt;</span>net8.0<span class="nt">&lt;/TargetFramework&gt;</span>
    <span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span>
    <span class="nt">&lt;ImplicitUsings&gt;</span>enable<span class="nt">&lt;/ImplicitUsings&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>

  <span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.AspNetCore.OpenApi"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Swashbuckle.AspNetCore"</span> <span class="na">Version=</span><span class="s">"6.5.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore.SqlServer"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.EntityFrameworkCore.Design"</span> <span class="na">Version=</span><span class="s">"8.0.0"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/ItemGroup&gt;</span>

  <span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;ProjectReference</span> <span class="na">Include=</span><span class="s">"..\..\..\Shared\EventBus\EventBus\EventBus.csproj"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/ItemGroup&gt;</span>

<span class="nt">&lt;/Project&gt;</span>


</code></pre></div></div>

<h2 id="step-5-docker-configuration">Step 5: Docker Configuration</h2>

<h3 id="create-docker-composeyml">Create docker-compose.yml</h3>

<p>Create the <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file to define the services, including SQL Server, Zookeeper, Kafka, Product Service, and Order Service. It should be placed in the root directory of your solution.</p>

<p><strong>docker-compose.yml</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.8'</span>

<span class="na">services</span><span class="pi">:</span>
  <span class="c1"># SQL Server</span>
  <span class="na">sqlserver</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">mcr.microsoft.com/mssql/server:2022-latest</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">SA_PASSWORD</span><span class="pi">:</span> <span class="s2">"</span><span class="s">YourPassword123!"</span>
      <span class="na">ACCEPT_EULA</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Y"</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">1433:1433"</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">sqlserver_data:/var/opt/mssql</span>
    <span class="na">healthcheck</span><span class="pi">:</span>
      <span class="na">test</span><span class="pi">:</span> <span class="s">/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "YourPassword123!" -C -Q "SELECT 1"</span>
      <span class="na">interval</span><span class="pi">:</span> <span class="s">10s</span>
      <span class="na">timeout</span><span class="pi">:</span> <span class="s">3s</span>
      <span class="na">retries</span><span class="pi">:</span> <span class="m">10</span>
      <span class="na">start_period</span><span class="pi">:</span> <span class="s">10s</span>

  <span class="c1"># Zookeeper (required for Kafka)</span>
  <span class="na">zookeeper</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">confluentinc/cp-zookeeper:7.4.0</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">ZOOKEEPER_CLIENT_PORT</span><span class="pi">:</span> <span class="m">2181</span>
      <span class="na">ZOOKEEPER_TICK_TIME</span><span class="pi">:</span> <span class="m">2000</span>

  <span class="c1"># Apache Kafka</span>
  <span class="na">kafka</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">confluentinc/cp-kafka:7.4.0</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">zookeeper</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">9092:9092"</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">KAFKA_BROKER_ID</span><span class="pi">:</span> <span class="m">1</span>
      <span class="na">KAFKA_ZOOKEEPER_CONNECT</span><span class="pi">:</span> <span class="s">zookeeper:2181</span>
      <span class="na">KAFKA_ADVERTISED_LISTENERS</span><span class="pi">:</span> <span class="s">PLAINTEXT://kafka:9092</span>
      <span class="na">KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR</span><span class="pi">:</span> <span class="m">1</span>
      <span class="na">KAFKA_AUTO_CREATE_TOPICS_ENABLE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>

  <span class="c1"># Product Service</span>
  <span class="na">productservice</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span>
      <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
      <span class="na">dockerfile</span><span class="pi">:</span> <span class="s">src/Services/ProductService/Dockerfile</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">5001:8080"</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="na">sqlserver</span><span class="pi">:</span>
        <span class="na">condition</span><span class="pi">:</span> <span class="s">service_healthy</span>
      <span class="na">kafka</span><span class="pi">:</span>
        <span class="na">condition</span><span class="pi">:</span> <span class="s">service_started</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">ASPNETCORE_ENVIRONMENT=Development</span>
      <span class="pi">-</span> <span class="s">ConnectionStrings__DefaultConnection=Server=sqlserver,1433;Database=ProductsDB;User Id=sa;Password=YourPassword123!;TrustServerCertificate=True;</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">on-failure</span>

  <span class="c1"># Order Service</span>
  <span class="na">orderservice</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span>
      <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
      <span class="na">dockerfile</span><span class="pi">:</span> <span class="s">src/Services/OrderService/Dockerfile</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">5002:8080"</span>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="na">sqlserver</span><span class="pi">:</span>
        <span class="na">condition</span><span class="pi">:</span> <span class="s">service_healthy</span>
      <span class="na">kafka</span><span class="pi">:</span>
        <span class="na">condition</span><span class="pi">:</span> <span class="s">service_started</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">ASPNETCORE_ENVIRONMENT=Development</span>
      <span class="pi">-</span> <span class="s">ConnectionStrings__DefaultConnection=Server=sqlserver,1433;Database=OrdersDB;User Id=sa;Password=YourPassword123!;TrustServerCertificate=True;</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">on-failure</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">sqlserver_data</span><span class="pi">:</span>

</code></pre></div></div>
<h3 id="create-dockerfiles-for-product-and-order-services">Create Dockerfiles for Product and Order Services</h3>

<p>Create <code class="language-plaintext highlighter-rouge">Dockerfile</code> for both Product Service and Order Service. Place them in their respective service directories.</p>

<p><strong>src/Services/ProductService/Dockerfile</strong></p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">mcr.microsoft.com/dotnet/aspnet:8.0</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">EXPOSE</span><span class="s"> 8080</span>

<span class="k">FROM</span><span class="w"> </span><span class="s">mcr.microsoft.com/dotnet/sdk:8.0</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span>
<span class="k">WORKDIR</span><span class="s"> /src</span>

<span class="c"># Copy project files</span>
<span class="k">COPY</span><span class="s"> ["src/Services/ProductService/ProductService/ProductService.csproj", "src/Services/ProductService/ProductService/"]</span>
<span class="k">COPY</span><span class="s"> ["src/Shared/EventBus/EventBus/EventBus.csproj", "src/Shared/EventBus/EventBus/"]</span>

<span class="c"># Restore dependencies</span>
<span class="k">RUN </span>dotnet restore <span class="s2">"src/Services/ProductService/ProductService/ProductService.csproj"</span>

<span class="c"># Copy source code</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">WORKDIR</span><span class="s"> "/src/src/Services/ProductService/ProductService"</span>
<span class="k">RUN </span>dotnet build <span class="s2">"ProductService.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/build

<span class="k">FROM</span><span class="w"> </span><span class="s">build</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">publish</span>
<span class="k">RUN </span>dotnet publish <span class="s2">"ProductService.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/publish

<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">final</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> --from=publish /app/publish .</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["dotnet", "ProductService.dll"]</span>
</code></pre></div></div>

<p><strong>src/Services/OrderService/Dockerfile</strong></p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">FROM</span><span class="w"> </span><span class="s">mcr.microsoft.com/dotnet/aspnet:8.0</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">base</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">EXPOSE</span><span class="s"> 8080</span>

<span class="k">FROM</span><span class="w"> </span><span class="s">mcr.microsoft.com/dotnet/sdk:8.0</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span>
<span class="k">WORKDIR</span><span class="s"> /src</span>

<span class="c"># Copy project files</span>
<span class="k">COPY</span><span class="s"> ["src/Services/OrderService/OrderService/OrderService.csproj", "src/Services/OrderService/OrderService/"]</span>
<span class="k">COPY</span><span class="s"> ["src/Shared/EventBus/EventBus/EventBus.csproj", "src/Shared/EventBus/EventBus/"]</span>

<span class="c"># Restore dependencies</span>
<span class="k">RUN </span>dotnet restore <span class="s2">"src/Services/OrderService/OrderService/OrderService.csproj"</span>

<span class="c"># Copy source code</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">WORKDIR</span><span class="s"> "/src/src/Services/OrderService/OrderService"</span>
<span class="k">RUN </span>dotnet build <span class="s2">"OrderService.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/build

<span class="k">FROM</span><span class="w"> </span><span class="s">build</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">publish</span>
<span class="k">RUN </span>dotnet publish <span class="s2">"OrderService.csproj"</span> <span class="nt">-c</span> Release <span class="nt">-o</span> /app/publish

<span class="k">FROM</span><span class="w"> </span><span class="s">base</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">final</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> --from=publish /app/publish .</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["dotnet", "OrderService.dll"]</span>

</code></pre></div></div>

<h3 id="create-dockerignore-file">Create .dockerignore file</h3>
<p>Create a <code class="language-plaintext highlighter-rouge">.dockerignore</code> file in the root directory to exclude unnecessary files from the Docker build context.
<strong>.dockerignore</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.vs
**/.vscode
**/**/bin
**/**/obj
**/.toolstarget
**/node_modules
**/npm-debug.log
**/Dockerfile*
**/docker-compose*
**/README.md
**/LICENSE
**/CHANGELOG.md
</code></pre></div></div>

<!-- Completed up to  -->

<h2 id="step-6-running-the-application">Step 6: Running the Application</h2>

<h3 id="build-and-run-with-docker-compose">Build and Run with Docker Compose</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Build and start all services</span>
docker-compose up <span class="nt">--build</span>

<span class="c"># Or run in detached mode</span>
docker-compose up <span class="nt">-d</span> <span class="nt">--build</span>
</code></pre></div></div>

<h3 id="verify-services-are-running">Verify Services are Running</h3>

<p>Check that all containers are running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose ps
</code></pre></div></div>

<p>You should see:</p>
<ul>
  <li>SQL Server running on port 1433</li>
  <li>Kafka and Zookeeper running</li>
  <li>ProductService running on port 5001</li>
  <li>OrderService running on port 5002</li>
</ul>

<p><strong>Note:</strong> Kafka is used for event-driven communication between services. You can use tools like Kafka UI (e.g., Kafdrop, Kafka Manager) to visualize topics and messages. Zookeeper is required for Kafka to manage its cluster state. To set up a Kafka UI, you can add another service in the <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file.</p>

<h2 id="step-7-testing-the-microservices">Step 7: Testing the Microservices</h2>

<h3 id="test-product-service">Test Product Service</h3>
<p>After running docker-compose, test the Product Service endpoints. Browse to http://localhost:5001/swagger to access the Swagger UI for Product Service. Now you can create a product with quantity in stock.</p>

<p><strong>Swagger UI:</strong>
<img src="/assets/images/posts/2026/product_service01.png" alt="" /></p>

<p><strong>Create Product:</strong>
<img src="/assets/images/posts/2026/product_service02.png" alt="" /></p>

<h3 id="test-order-service">Test Order Service</h3>
<p><strong>Swagger UI:</strong>
<img src="/assets/images/posts/2026/order_service01.png" alt="" /></p>

<p><strong>Create Order:</strong>
<img src="/assets/images/posts/2026/order_service02.png" alt="" /></p>

<h2 id="step-8-monitoring-kafka-events">Step 8: Monitoring Kafka Events</h2>

<p>You can check if events are being published to Kafka topics:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List topics</span>
docker <span class="nb">exec</span> <span class="nt">-it</span> &lt;kafka-container-id&gt; kafka-topics <span class="nt">--list</span> <span class="nt">--bootstrap-server</span> localhost:9092

<span class="c"># Monitor events</span>
docker <span class="nb">exec</span> <span class="nt">-it</span> &lt;kafka-container-id&gt; kafka-console-consumer <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> ecommerce.ProductCreatedEvent <span class="nt">--from-beginning</span>
</code></pre></div></div>

<h2 id="key-benefits-of-this-architecture">Key Benefits of This Architecture</h2>

<ol>
  <li><strong>Loose Coupling</strong>: Services communicate through events, not direct API calls</li>
  <li><strong>Scalability</strong>: Each service can be scaled independently</li>
  <li><strong>Resilience</strong>: If one service fails, others continue to operate</li>
  <li><strong>Technology Diversity</strong>: Each service can use different technologies</li>
  <li><strong>Database Independence</strong>: Each service has its own database</li>
  <li><strong>Event-Driven</strong>: Asynchronous processing improves performance</li>
</ol>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="common-issues">Common Issues:</h3>

<ol>
  <li><strong>Services can’t connect to SQL Server</strong>: Make sure the connection string is correct and SQL Server container is running</li>
  <li><strong>Kafka connection issues</strong>: Verify Kafka and Zookeeper containers are healthy</li>
  <li><strong>Port conflicts</strong>: Check if ports 1433, 9092, 5001, 5002 are available</li>
  <li><strong>Database creation fails</strong>: Ensure SQL Server is fully initialized before services start</li>
</ol>

<h3 id="useful-docker-commands">Useful Docker Commands:</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># View logs</span>
docker-compose logs productservice
docker-compose logs orderservice

<span class="c"># Stop all services</span>
docker-compose down

<span class="c"># Clean up volumes</span>
docker-compose down <span class="nt">-v</span>

<span class="c"># Rebuild specific service</span>
docker-compose build productservice
</code></pre></div></div>

<h2 id="next-steps">Next Steps</h2>

<p>To extend this application, you could:</p>

<ol>
  <li>Add API Gateway using Ocelot or YARP</li>
  <li>Implement Circuit Breaker pattern with Polly</li>
  <li>Add distributed tracing with OpenTelemetry</li>
  <li>Implement event sourcing</li>
  <li>Add authentication and authorization</li>
  <li>Create a web frontend with React or Angular</li>
  <li>Add monitoring with Prometheus and Grafana</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>In this tutorial, we’ve successfully built a microservices application using ASP.NET Core, Apache Kafka, and Docker. We created two independent services that communicate through events, each with its own database. This architecture provides the foundation for building scalable, maintainable applications.</p>

<p>The event-driven approach using Kafka ensures loose coupling between services while providing reliable message delivery. Docker containers make the application portable and easy to deploy across different environments.</p>

<p>This pattern can be extended to handle more complex business scenarios and larger-scale applications while maintaining the benefits of microservices architecture.</p>

<p>Please leave your comments and suggestions for further improvements! Share this tutorial if you found it helpful. Happy coding!</p>

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/main/MicroservicesDemo">Complete Source Code on GitHub</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term="ASP.NET Core" /><category term="Microservices" /><category term="Docker" /><category term="Software Architecture" /><category term="aspnetcore" /><category term="microservices" /><category term="docker" /><category term="software-architecture" /><category term="designpattern" /><category term="csharp" /><category term="dotnet" /><category term="devops" /><summary type="html"><![CDATA[This step-by-step tutorial demonstrates how to build a sample microservices application using ASP.NET Core, Apache Kafka, and Docker. You will learn how to design independent services, implement event-driven communication, integrate SQL Server with Entity Framework Core, and run everything in containers for local development.]]></summary></entry><entry><title type="html">Implementing Distributed Transactions in Microservices: SAGA Pattern with RabbitMQ and ASP.NET Core</title><link href="https://mahedee.net/implementing-distributed-transactions-saga-pattern-rabbitmq-aspnetcore.md/" rel="alternate" type="text/html" title="Implementing Distributed Transactions in Microservices: SAGA Pattern with RabbitMQ and ASP.NET Core" /><published>2026-02-17T00:00:00-05:00</published><updated>2026-02-17T00:00:00-05:00</updated><id>https://mahedee.net/implementing-distributed-transactions-saga-pattern-rabbitmq-aspnetcore.md</id><content type="html" xml:base="https://mahedee.net/implementing-distributed-transactions-saga-pattern-rabbitmq-aspnetcore.md/"><![CDATA[<p>Modern microservices architectures bring tremendous benefits in scalability and maintainability, but they also introduce a critical challenge: <strong>how do you maintain data consistency across multiple services and databases?</strong> Traditional ACID transactions work perfectly within a single database, but they break down when operations span multiple distributed services. Consider an e-commerce scenario where placing an order involves updating inventory, processing payment, and creating order records across different services—if any step fails after others succeed, you’re left with inconsistent data. The <strong>SAGA pattern</strong> solves this problem by coordinating distributed transactions through a sequence of local transactions, each with compensation logic to handle failures gracefully. In this comprehensive guide, you’ll learn how to implement the SAGA choreography pattern using <strong>RabbitMQ</strong> for event-driven messaging and <strong>ASP.NET Core</strong> for building resilient microservices that maintain data consistency even when things go wrong.</p>

<p><strong>Understanding the SAGA Pattern</strong></p>

<p>The <strong>SAGA pattern</strong> is a distributed transaction management approach that breaks down a complex business transaction into a sequence of smaller, local transactions. Unlike traditional ACID transactions that lock resources across multiple databases, SAGA ensures data consistency through a choreographed series of events and compensating actions.</p>

<p><strong>How SAGA Works:</strong></p>

<ol>
  <li><strong>Local Transactions</strong>: Each service performs its own local database transaction</li>
  <li><strong>Event Publishing</strong>: After successful completion, the service publishes an event or message</li>
  <li><strong>Chain Reaction</strong>: Other services listen for these events and execute their own transactions</li>
  <li><strong>Compensation Logic</strong>: If any transaction fails, previously completed transactions are undone through compensating actions</li>
</ol>

<p><strong>Key Benefits:</strong></p>
<ul>
  <li><strong>No Distributed Locks</strong>: Avoids the complexity and performance issues of distributed locking</li>
  <li><strong>Fault Tolerance</strong>: System can recover gracefully from failures</li>
  <li><strong>Scalability</strong>: Each service manages its own data independently</li>
  <li><strong>Eventual Consistency</strong>: Data becomes consistent over time, not immediately</li>
</ul>

<p><strong>SAGA Implementation Approaches</strong>
SAGA can be implemented using two distinct patterns, each with its own trade-offs:</p>

<p><strong>Choreography Pattern</strong>
Services communicate directly with each other through events, without central coordination. Each service knows what events to listen for and what events to publish next.</p>

<p><img src="/assets/images/posts/2026/choreography-pattern.png" alt="" /></p>

<p>Fig - Choreography saga (Collected)</p>

<p><strong>Characteristics:</strong></p>
<ul>
  <li>Decentralized control</li>
  <li>Services are loosely coupled</li>
  <li>No single point of failure</li>
  <li>More complex to track transaction state</li>
</ul>

<p><strong>Orchestration Pattern</strong> 
A central orchestrator service coordinates the entire transaction flow, telling each service what to do and when.</p>

<p><img src="/assets/images/posts/2026/orchestrator.png" alt="" /></p>

<p>Fig - Orchestration saga (Collected)</p>

<p><strong>Characteristics:</strong></p>
<ul>
  <li>Centralized control and monitoring</li>
  <li>Easier to track transaction progress</li>
  <li>Single point of failure (the orchestrator)</li>
  <li>Orchestrator can become a bottleneck</li>
</ul>

<p>In this tutorial, we’ll implement the <strong>choreography pattern</strong> as it promotes better service autonomy and scalability in microservices architectures.</p>

<h2 id="implementation-of-choreography-pattern">Implementation of Choreography Pattern</h2>

<p>In this section, we’ll build a comprehensive e-commerce application demonstrating the SAGA choreography pattern with three microservices communicating through RabbitMQ to handle a complete order-to-payment flow.</p>

<h3 id="project-overview"><strong>Project Overview</strong></h3>

<p>We’ll implement a realistic e-commerce scenario with:</p>

<ul>
  <li><strong>Order Service</strong>: Manages customer orders and orchestrates the overall flow</li>
  <li><strong>Catalog Service</strong>: Manages product inventory and stock reservations</li>
  <li><strong>Payment Service</strong>: Handles payment processing and transaction management</li>
</ul>

<p><strong>Enhanced Transaction Flow:</strong></p>
<ol>
  <li>Customer places order → Order Service creates order record</li>
  <li>Order Service publishes “OrderCreated” event → RabbitMQ</li>
  <li>Catalog Service receives event → Validates and reserves inventory</li>
  <li>Catalog Service publishes “InventoryReserved” event → RabbitMQ</li>
  <li>Payment Service receives event → Processes payment transaction</li>
  <li>Payment Service publishes “PaymentProcessed” event → RabbitMQ</li>
  <li>Order &amp; Catalog Services receive payment result → Finalize or compensate</li>
</ol>

<p><strong>Compensation Scenarios:</strong></p>
<ul>
  <li><strong>Inventory Failure</strong>: Order cancelled immediately</li>
  <li><strong>Payment Failure</strong>: Inventory released, order cancelled</li>
  <li><strong>Service Timeout</strong>: Automatic compensation after timeout period</li>
</ul>

<h3 id="prerequisites"><strong>Prerequisites</strong></h3>

<ul>
  <li><strong>Visual Studio 2026</strong> or <strong>Visual Studio Code</strong> with C# extensions</li>
  <li><strong>.NET 10.0</strong> (Latest LTS) or <strong>.NET 11.0</strong> (Preview)</li>
  <li><strong>Docker Desktop</strong> (for RabbitMQ container)</li>
  <li><strong>Entity Framework Core 10.0+</strong> with SQLite provider</li>
  <li><strong>SQLite database for simplicity</strong></li>
</ul>

<h2 id="step-1-setup-rabbitmq-with-docker"><strong>Step 1: Setup RabbitMQ with Docker</strong></h2>

<p>Start RabbitMQ in a Docker container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-d</span> <span class="nt">--hostname</span> ecommerce-rabbit <span class="nt">--name</span> saga-rabbitmq <span class="nt">-p</span> 15672:15672 <span class="nt">-p</span> 5672:5672 rabbitmq:4-management
</code></pre></div></div>
<p><strong>Verify RabbitMQ:</strong></p>
<ul>
  <li>Open browser to <code class="language-plaintext highlighter-rouge">http://localhost:15672</code></li>
  <li>Login with username: <code class="language-plaintext highlighter-rouge">guest</code>, password: <code class="language-plaintext highlighter-rouge">guest</code></li>
</ul>

<h2 id="step-2-create-solution-structure"><strong>Step 2: Create Solution Structure</strong></h2>

<p>Create the solution with proper project structure:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create solution</span>
dotnet new sln <span class="nt">-n</span> EcommerceSaga

<span class="c"># Create projects with .NET 10 framework and traditional controllers</span>
dotnet new webapi <span class="nt">-n</span> Order.API <span class="nt">--use-controllers</span> <span class="nt">--use-program-main</span>
dotnet new webapi <span class="nt">-n</span> Catalog.API <span class="nt">--use-controllers</span> <span class="nt">--use-program-main</span>
dotnet new webapi <span class="nt">-n</span> Payment.API <span class="nt">--use-controllers</span> <span class="nt">--use-program-main</span>
dotnet new classlib <span class="nt">-n</span> Shared

<span class="c"># Add projects to solution</span>
dotnet sln add Order.API/Order.API.csproj
dotnet sln add Catalog.API/Catalog.API.csproj
dotnet sln add Payment.API/Payment.API.csproj
dotnet sln add Shared/Shared.csproj
</code></pre></div></div>

<p><strong>Final structure:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EcommerceSaga/
├── EcommerceSaga.sln
├── Order.API/
├── Catalog.API/
├── Payment.API/
└── Shared/
</code></pre></div></div>

<p>This structure allows us to share common models and utilities in the <strong>Shared</strong> project while keeping each microservice isolated.</p>

<ul>
  <li>Run the following command to build the entire solution (if you have a .sln file):
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet build EcommerceSaga.sln
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="step-3-create-shared-models"><strong>Step 3: Create Shared Models</strong></h2>

<p>Add the following models in the <strong>Shared</strong> project:</p>

<h3 id="modelsordercreatedeventcs"><strong>Models/OrderCreatedEvent.cs</strong></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Shared.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderCreatedEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">ProductName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalAmount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="modelsinventoryreservedeventcs"><strong>Models/InventoryReservedEvent.cs</strong></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Shared.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">InventoryReservedEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">bool</span> <span class="n">IsSuccess</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Message</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalAmount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ReservedQuantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="modelspaymentprocessedeventcs"><strong>Models/PaymentProcessedEvent.cs</strong></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Shared.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentProcessedEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">bool</span> <span class="n">IsSuccess</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">TransactionId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Amount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Message</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="modelsinventoryreleaseeventcs"><strong>Models/InventoryReleaseEvent.cs</strong></h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Shared.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">InventoryReleaseEvent</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Reason</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="step-4-build-the-order-service"><strong>Step 4: Build the Order Service</strong></h2>

<h3 id="install-required-packages"><strong>Install Required Packages</strong></h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>Order.API
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Plain.RabbitMQ
dotnet add package Swashbuckle.AspNetCore
<span class="c"># Note: Using System.Text.Json (built-in) instead of Newtonsoft.Json</span>
dotnet add reference ../Shared/Shared.csproj
</code></pre></div></div>

<h3 id="modelsordercs"><strong>Models/Order.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations.Schema</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Order</span>
    <span class="p">{</span>
        <span class="p">[</span><span class="n">Key</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">DatabaseGenerated</span><span class="p">(</span><span class="n">DatabaseGeneratedOption</span><span class="p">.</span><span class="n">Identity</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="n">required</span> <span class="kt">string</span> <span class="n">ProductName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">Column</span><span class="p">(</span><span class="n">TypeName</span> <span class="p">=</span> <span class="s">"decimal(18,2)"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">Column</span><span class="p">(</span><span class="n">TypeName</span> <span class="p">=</span> <span class="s">"decimal(18,2)"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">TotalAmount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">"Pending"</span><span class="p">;</span> <span class="c1">// Pending, InventoryReserved, PaymentProcessing, Confirmed, Cancelled</span>
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
        <span class="k">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">UpdatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="n">required</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="dataorderdbcontextcs"><strong>Data/OrderDbContext.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">OrderDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">OrderDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
        <span class="p">{</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Models</span><span class="p">.</span><span class="n">Order</span><span class="p">&gt;</span> <span class="n">Orders</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Models</span><span class="p">.</span><span class="n">Order</span><span class="p">&gt;(</span><span class="n">entity</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">ProductId</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Status</span><span class="p">).</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">20</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">ProductName</span><span class="p">).</span><span class="nf">HasMaxLength</span><span class="p">(</span><span class="m">200</span><span class="p">);</span>
            <span class="p">});</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="servicesinventoryresponselistenercs"><strong>Services/InventoryResponseListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">InventoryResponseListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_subscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryResponseListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">InventoryResponseListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">subscriber</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryResponseListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span> <span class="p">=</span> <span class="n">subscriber</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessInventoryResponse</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Response Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessInventoryResponse</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received inventory response: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">InventoryReservedEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">response</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize inventory response"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderDbContext</span><span class="p">&gt;();</span>

                <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FirstOrDefault</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s"> not found"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Move to next step - inventory reserved, waiting for payment</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="s">"InventoryReserved"</span><span class="p">;</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> inventory reserved, awaiting payment processing"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="c1">// Compensation: Cancel order due to inventory failure</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="s">"Cancelled"</span><span class="p">;</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> cancelled due to inventory failure: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">Message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>
                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing inventory response"</span><span class="p">);</span>
                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Response Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>


</code></pre></div></div>

<h3 id="servicespaymentresponselistenercs"><strong>Services/PaymentResponseListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentResponseListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_paymentSubscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IPublisher</span> <span class="n">_publisher</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentResponseListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">PaymentResponseListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">paymentSubscriber</span><span class="p">,</span>
            <span class="n">IPublisher</span> <span class="n">publisher</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentResponseListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_paymentSubscriber</span> <span class="p">=</span> <span class="n">paymentSubscriber</span><span class="p">;</span>
            <span class="n">_publisher</span> <span class="p">=</span> <span class="n">publisher</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_paymentSubscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessPaymentResponse</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Payment Response Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessPaymentResponse</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received payment response: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">PaymentProcessedEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">response</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize payment response"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderDbContext</span><span class="p">&gt;();</span>

                <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FirstOrDefault</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">order</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s"> not found"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Payment successful - confirm order</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="s">"Confirmed"</span><span class="p">;</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> confirmed after successful payment: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="c1">// Payment failed - compensate by cancelling order and releasing inventory</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="s">"Cancelled"</span><span class="p">;</span>
                    <span class="n">order</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                    
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> cancelled due to payment failure: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">Message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

                    <span class="c1">// Publish inventory release event for compensation</span>
                    <span class="kt">var</span> <span class="n">releaseEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">InventoryReleaseEvent</span>
                    <span class="p">{</span>
                        <span class="n">OrderId</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">,</span>
                        <span class="n">ProductId</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                        <span class="n">Quantity</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span>
                        <span class="n">Reason</span> <span class="p">=</span> <span class="s">"Payment failed"</span>
                    <span class="p">};</span>

                    <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                        <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">releaseEvent</span><span class="p">),</span>
                        <span class="s">"inventory.release"</span><span class="p">,</span>
                        <span class="k">null</span><span class="p">);</span>

                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Published inventory release event for order </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>
                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing payment response"</span><span class="p">);</span>
                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Payment Response Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>


</code></pre></div></div>

<h3 id="controllersorderscontrollercs"><strong>Controllers/OrdersController.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrdersController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">OrderDbContext</span> <span class="n">_context</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IPublisher</span> <span class="n">_publisher</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">OrdersController</span><span class="p">(</span>
            <span class="n">OrderDbContext</span> <span class="n">context</span><span class="p">,</span> 
            <span class="n">IPublisher</span> <span class="n">publisher</span><span class="p">,</span> 
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrdersController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
            <span class="n">_publisher</span> <span class="p">=</span> <span class="n">publisher</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Models</span><span class="p">.</span><span class="n">Order</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetOrders</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">OrderByDescending</span><span class="p">(</span><span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">CreatedAt</span><span class="p">).</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Models</span><span class="p">.</span><span class="n">Order</span><span class="p">&gt;&gt;</span> <span class="nf">GetOrder</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">order</span> <span class="p">==</span> <span class="k">null</span> <span class="p">?</span> <span class="nf">NotFound</span><span class="p">()</span> <span class="p">:</span> <span class="n">order</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Models</span><span class="p">.</span><span class="n">Order</span><span class="p">&gt;&gt;</span> <span class="nf">CreateOrder</span><span class="p">(</span><span class="n">CreateOrderRequest</span> <span class="n">request</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">order</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Models</span><span class="p">.</span><span class="n">Order</span>
                <span class="p">{</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                    <span class="n">ProductName</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">ProductName</span><span class="p">,</span>
                    <span class="n">Quantity</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span>
                    <span class="n">UnitPrice</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">,</span>
                    <span class="n">TotalAmount</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">Quantity</span> <span class="p">*</span> <span class="n">request</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">,</span>
                    <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">CustomerEmail</span>
                <span class="p">};</span>

                <span class="n">_context</span><span class="p">.</span><span class="n">Orders</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
                <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>

                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Order </span><span class="p">{</span><span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">}</span><span class="s"> created, publishing OrderCreated event"</span><span class="p">);</span>

                <span class="c1">// Publish order created event</span>
                <span class="kt">var</span> <span class="n">orderCreatedEvent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OrderCreatedEvent</span>
                <span class="p">{</span>
                    <span class="n">OrderId</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span><span class="p">,</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                    <span class="n">ProductName</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">ProductName</span><span class="p">,</span>
                    <span class="n">Quantity</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Quantity</span><span class="p">,</span>
                    <span class="n">UnitPrice</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">UnitPrice</span><span class="p">,</span>
                    <span class="n">TotalAmount</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span>
                    <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">CustomerEmail</span>
                <span class="p">};</span>

                <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                    <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">orderCreatedEvent</span><span class="p">),</span>
                    <span class="s">"order.created"</span><span class="p">,</span>
                    <span class="k">null</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetOrder</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">order</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="n">order</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error creating order"</span><span class="p">);</span>
                <span class="k">return</span> <span class="nf">StatusCode</span><span class="p">(</span><span class="m">500</span><span class="p">,</span> <span class="s">"Internal server error"</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">CreateOrderRequest</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">ProductName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Quantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">UnitPrice</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="appsettingsjson"><strong>appsettings.json</strong></h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.EntityFrameworkCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data Source=orders.db"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="programcs"><strong>Program.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Order.API.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">RabbitMQ.Client</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Order.API</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

            <span class="c1">// Add services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">();</span>

            <span class="c1">// Database</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">OrderDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
                <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlite</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

            <span class="c1">// RabbitMQ Configuration</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(</span>
                <span class="k">new</span> <span class="nf">ConnectionProvider</span><span class="p">(</span><span class="s">"amqp://guest:guest@localhost:5672"</span><span class="p">));</span>

            <span class="c1">// Publisher for Order events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IPublisher</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Publisher</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"order.exchange"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Inventory events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"inventory.exchange"</span><span class="p">,</span>
                    <span class="s">"inventory.response.queue"</span><span class="p">,</span>
                    <span class="s">"inventory.reserved"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Payment events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddKeyedSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="s">"PaymentSubscriber"</span><span class="p">,</span> <span class="p">(</span><span class="n">provider</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"payment.exchange"</span><span class="p">,</span>
                    <span class="s">"payment.response.queue"</span><span class="p">,</span>
                    <span class="s">"payment.processed"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Background Services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">InventoryResponseListener</span><span class="p">&gt;();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">PaymentResponseListener</span><span class="p">&gt;();</span>

            <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

            <span class="c1">// Configure the HTTP request pipeline</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

            <span class="c1">// Ensure database is created</span>
            <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">OrderDbContext</span><span class="p">&gt;();</span>
                <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<ul>
  <li>Run the following command to build the Order.API project:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet build
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="step-5-build-the-catalog-service"><strong>Step 5: Build the Catalog Service</strong></h2>

<h3 id="install-required-packages-1"><strong>Install Required Packages</strong></h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ../Catalog.API
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Plain.RabbitMQ
dotnet add package Swashbuckle.AspNetCore
<span class="c"># Note: Using System.Text.Json (built-in with .NET 10) instead of Newtonsoft.Json</span>
dotnet add reference ../Shared/Shared.csproj
</code></pre></div></div>

<h3 id="modelsproductcs"><strong>Models/Product.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations.Schema</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Product</span>
    <span class="p">{</span>
        <span class="p">[</span><span class="n">Key</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">DatabaseGenerated</span><span class="p">(</span><span class="n">DatabaseGeneratedOption</span><span class="p">.</span><span class="n">Identity</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">200</span><span class="p">)]</span>
        <span class="k">public</span> <span class="n">required</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">1000</span><span class="p">)]</span>
        <span class="k">public</span> <span class="n">required</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">Column</span><span class="p">(</span><span class="n">TypeName</span> <span class="p">=</span> <span class="s">"decimal(18,2)"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">int</span> <span class="n">AvailableStock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ReservedStock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">MaxStockThreshold</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="m">100</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
        <span class="k">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">UpdatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="c1">// Computed property for total stock</span>
        <span class="p">[</span><span class="n">NotMapped</span><span class="p">]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">TotalStock</span> <span class="p">=&gt;</span> <span class="n">AvailableStock</span> <span class="p">+</span> <span class="n">ReservedStock</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="datacatalogdbcontextcs"><strong>Data/CatalogDbContext.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">CatalogDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">CatalogDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">CatalogDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
        <span class="p">{</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="n">Products</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Seed some sample data</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;().</span><span class="nf">HasData</span><span class="p">(</span>
                <span class="k">new</span> <span class="n">Product</span> 
                <span class="p">{</span> 
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span> 
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Gaming Laptop"</span><span class="p">,</span> 
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"High-performance gaming laptop with RTX graphics"</span><span class="p">,</span> 
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">1299.99m</span><span class="p">,</span> 
                    <span class="n">AvailableStock</span> <span class="p">=</span> <span class="m">10</span><span class="p">,</span> 
                    <span class="n">ReservedStock</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span>
                    <span class="n">MaxStockThreshold</span> <span class="p">=</span> <span class="m">20</span> 
                <span class="p">},</span>
                <span class="k">new</span> <span class="n">Product</span> 
                <span class="p">{</span> 
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">2</span><span class="p">,</span> 
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Wireless Mouse"</span><span class="p">,</span> 
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"Ergonomic wireless gaming mouse"</span><span class="p">,</span> 
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">79.99m</span><span class="p">,</span> 
                    <span class="n">AvailableStock</span> <span class="p">=</span> <span class="m">25</span><span class="p">,</span> 
                    <span class="n">ReservedStock</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span>
                    <span class="n">MaxStockThreshold</span> <span class="p">=</span> <span class="m">50</span> 
                <span class="p">},</span>
                <span class="k">new</span> <span class="n">Product</span> 
                <span class="p">{</span> 
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">3</span><span class="p">,</span> 
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Mechanical Keyboard"</span><span class="p">,</span> 
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"RGB mechanical keyboard with Cherry MX switches"</span><span class="p">,</span> 
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">159.99m</span><span class="p">,</span> 
                    <span class="n">AvailableStock</span> <span class="p">=</span> <span class="m">15</span><span class="p">,</span> 
                    <span class="n">ReservedStock</span> <span class="p">=</span> <span class="m">0</span><span class="p">,</span>
                    <span class="n">MaxStockThreshold</span> <span class="p">=</span> <span class="m">30</span> 
                <span class="p">}</span>
            <span class="p">);</span>

            <span class="c1">// Add index for better performance</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;()</span>
                <span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="servicesordercreatedlistenercs"><strong>Services/OrderCreatedListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">OrderCreatedListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_subscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IPublisher</span> <span class="n">_publisher</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrderCreatedListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">OrderCreatedListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">subscriber</span><span class="p">,</span>
            <span class="n">IPublisher</span> <span class="n">publisher</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">OrderCreatedListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span> <span class="p">=</span> <span class="n">subscriber</span><span class="p">;</span>
            <span class="n">_publisher</span> <span class="p">=</span> <span class="n">publisher</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessOrderCreated</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Order Created Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessOrderCreated</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">InventoryReservedEvent</span><span class="p">?</span> <span class="n">response</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received order created event: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">orderCreated</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">OrderCreatedEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">orderCreated</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize order created event"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">CatalogDbContext</span><span class="p">&gt;();</span>

                <span class="n">response</span> <span class="p">=</span> <span class="k">new</span> <span class="n">InventoryReservedEvent</span>
                <span class="p">{</span>
                    <span class="n">OrderId</span> <span class="p">=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">OrderId</span><span class="p">,</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                    <span class="n">TotalAmount</span> <span class="p">=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span>
                    <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">CustomerEmail</span>
                <span class="p">};</span>

                <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Find</span><span class="p">(</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">$"Product with ID </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found"</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found for order </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span> <span class="p">&lt;</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">$"Insufficient stock. Available: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">}</span><span class="s">, Requested: </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Insufficient stock for product </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s">. Available: </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">}</span><span class="s">, Requested: </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="c1">// Reserve inventory (don't commit yet, wait for payment confirmation)</span>
                    <span class="kt">var</span> <span class="n">originalStock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">;</span>
                    <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span> <span class="p">-=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
                    <span class="n">product</span><span class="p">.</span><span class="n">ReservedStock</span> <span class="p">+=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
                    <span class="n">product</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                    
                    <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>

                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">"Inventory reserved successfully, awaiting payment"</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">ReservedQuantity</span> <span class="p">=</span> <span class="n">orderCreated</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
                    
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Inventory reserved for product </span><span class="p">{</span><span class="n">orderCreated</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s">. "</span> <span class="p">+</span>
                                         <span class="s">$"Available stock changed from </span><span class="p">{</span><span class="n">originalStock</span><span class="p">}</span><span class="s"> to </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Publish inventory response</span>
                <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                    <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">response</span><span class="p">),</span>
                    <span class="s">"inventory.reserved"</span><span class="p">,</span>
                    <span class="k">null</span><span class="p">);</span>

                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing order created event"</span><span class="p">);</span>
                
                <span class="c1">// Publish failure response if we have basic info</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">response</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">"Internal error processing inventory reservation"</span><span class="p">;</span>
                    
                    <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                        <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">response</span><span class="p">),</span>
                        <span class="s">"inventory.reserved"</span><span class="p">,</span>
                        <span class="k">null</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Order Created Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="servicesinventoryreleaselistenercs"><strong>Services/InventoryReleaseListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">InventoryReleaseListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_subscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryReleaseListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">InventoryReleaseListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">subscriber</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryReleaseListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span> <span class="p">=</span> <span class="n">subscriber</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessInventoryRelease</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Release Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessInventoryRelease</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received inventory release event: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">releaseEvent</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">InventoryReleaseEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">releaseEvent</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize inventory release event"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">CatalogDbContext</span><span class="p">&gt;();</span>

                <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Find</span><span class="p">(</span><span class="n">releaseEvent</span><span class="p">.</span><span class="n">ProductId</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">$"Product </span><span class="p">{</span><span class="n">releaseEvent</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> not found for inventory release"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">true</span><span class="p">;</span> <span class="c1">// Consider it processed</span>
                <span class="p">}</span>

                <span class="c1">// Release reserved inventory back to available stock</span>
                <span class="kt">var</span> <span class="n">beforeAvailable</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">;</span>
                <span class="kt">var</span> <span class="n">beforeReserved</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">ReservedStock</span><span class="p">;</span>
                
                <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span> <span class="p">+=</span> <span class="n">releaseEvent</span><span class="p">.</span><span class="n">Quantity</span><span class="p">;</span>
                <span class="n">product</span><span class="p">.</span><span class="n">ReservedStock</span> <span class="p">=</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Max</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">product</span><span class="p">.</span><span class="n">ReservedStock</span> <span class="p">-</span> <span class="n">releaseEvent</span><span class="p">.</span><span class="n">Quantity</span><span class="p">);</span>
                <span class="n">product</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
                
                <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>

                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Released inventory for product </span><span class="p">{</span><span class="n">releaseEvent</span><span class="p">.</span><span class="n">ProductId</span><span class="p">}</span><span class="s"> due to: </span><span class="p">{</span><span class="n">releaseEvent</span><span class="p">.</span><span class="n">Reason</span><span class="p">}</span><span class="s">. "</span> <span class="p">+</span>
                                     <span class="s">$"Available: </span><span class="p">{</span><span class="n">beforeAvailable</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">}</span><span class="s">, "</span> <span class="p">+</span>
                                     <span class="s">$"Reserved: </span><span class="p">{</span><span class="n">beforeReserved</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">product</span><span class="p">.</span><span class="n">ReservedStock</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing inventory release event"</span><span class="p">);</span>
                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Release Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="servicespaymentconfirmationlistenercs"><strong>Services/PaymentConfirmationListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentConfirmationListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_subscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentConfirmationListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">PaymentConfirmationListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">subscriber</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentConfirmationListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span> <span class="p">=</span> <span class="n">subscriber</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessPaymentConfirmation</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Payment Confirmation Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessPaymentConfirmation</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received payment confirmation: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">paymentEvent</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">PaymentProcessedEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">paymentEvent</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize payment event"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">paymentEvent</span><span class="p">.</span><span class="n">IsSuccess</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Payment successful - inventory reservation is now committed</span>
                    <span class="c1">// No action needed, inventory was already reserved</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Payment confirmed for order </span><span class="p">{</span><span class="n">paymentEvent</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">, inventory reservation committed"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="c1">// Note: If payment fails, the InventoryReleaseListener will handle releasing the inventory</span>

                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing payment confirmation"</span><span class="p">);</span>
                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Payment Confirmation Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="controllersproductscontrollercs"><strong>Controllers/ProductsController.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductsController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">CatalogDbContext</span> <span class="n">_context</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductsController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">ProductsController</span><span class="p">(</span><span class="n">CatalogDbContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ProductsController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetProducts</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">OrderBy</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Name</span><span class="p">).</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">GetProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">product</span> <span class="p">==</span> <span class="k">null</span> <span class="p">?</span> <span class="nf">NotFound</span><span class="p">()</span> <span class="p">:</span> <span class="n">product</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}/stock"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateStock</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">UpdateStockRequest</span> <span class="n">request</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="kt">var</span> <span class="n">oldAvailableStock</span> <span class="p">=</span> <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">;</span>
            <span class="n">product</span><span class="p">.</span><span class="n">AvailableStock</span> <span class="p">=</span> <span class="n">request</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">;</span>
            <span class="n">product</span><span class="p">.</span><span class="n">UpdatedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>

            <span class="k">try</span>
            <span class="p">{</span>
                <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Stock updated for product </span><span class="p">{</span><span class="n">id</span><span class="p">}</span><span class="s">: </span><span class="p">{</span><span class="n">oldAvailableStock</span><span class="p">}</span><span class="s"> → </span><span class="p">{</span><span class="n">request</span><span class="p">.</span><span class="n">AvailableStock</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">DbUpdateConcurrencyException</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">if</span> <span class="p">(!</span><span class="nf">ProductExists</span><span class="p">(</span><span class="n">id</span><span class="p">))</span>
                    <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProductExists</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Any</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">id</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">UpdateStockRequest</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">AvailableStock</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="appsettingsjson-1"><strong>appsettings.json</strong></h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.EntityFrameworkCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data Source=catalog.db"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="programcs-1"><strong>Program.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Catalog.API.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">RabbitMQ.Client</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Catalog.API</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

            <span class="c1">// Add services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">();</span>

            <span class="c1">// Database</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">CatalogDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
                <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlite</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

            <span class="c1">// RabbitMQ Configuration</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(</span>
                <span class="k">new</span> <span class="nf">ConnectionProvider</span><span class="p">(</span><span class="s">"amqp://guest:guest@localhost:5672"</span><span class="p">));</span>

            <span class="c1">// Publisher for Inventory events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IPublisher</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Publisher</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"inventory.exchange"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Order events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"order.exchange"</span><span class="p">,</span>
                    <span class="s">"order.created.queue"</span><span class="p">,</span>
                    <span class="s">"order.created"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Inventory Release events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddKeyedSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="s">"ReleaseSubscriber"</span><span class="p">,</span> <span class="p">(</span><span class="n">provider</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"order.exchange"</span><span class="p">,</span>
                    <span class="s">"inventory.release.queue"</span><span class="p">,</span>
                    <span class="s">"inventory.release"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Payment events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddKeyedSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="s">"PaymentSubscriber"</span><span class="p">,</span> <span class="p">(</span><span class="n">provider</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"payment.exchange"</span><span class="p">,</span>
                    <span class="s">"payment.confirmation.queue"</span><span class="p">,</span>
                    <span class="s">"payment.processed"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Background Services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">OrderCreatedListener</span><span class="p">&gt;();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">InventoryReleaseListener</span><span class="p">&gt;();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">PaymentConfirmationListener</span><span class="p">&gt;();</span>

            <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

            <span class="c1">// Configure the HTTP request pipeline</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

            <span class="c1">// Ensure database is created and seeded</span>
            <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">CatalogDbContext</span><span class="p">&gt;();</span>
                <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<!-- Completed up to -->

<h2 id="step-6-build-the-payment-service"><strong>Step 6: Build the Payment Service</strong></h2>

<h3 id="install-required-packages-2"><strong>Install Required Packages</strong></h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ../Payment.API
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Plain.RabbitMQ
dotnet add package Swashbuckle.AspNetCore
<span class="c"># Note: Using System.Text.Json (built-in with .NET 10) instead of Newtonsoft.Json</span>
dotnet add reference ../Shared/Shared.csproj
</code></pre></div></div>

<h3 id="modelspaymenttransactioncs"><strong>Models/PaymentTransaction.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.ComponentModel.DataAnnotations.Schema</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API.Models</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentTransaction</span>
    <span class="p">{</span>
        <span class="p">[</span><span class="n">Key</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">DatabaseGenerated</span><span class="p">(</span><span class="n">DatabaseGeneratedOption</span><span class="p">.</span><span class="n">Identity</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">int</span> <span class="n">OrderId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">ProductId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">Column</span><span class="p">(</span><span class="n">TypeName</span> <span class="p">=</span> <span class="s">"decimal(18,2)"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Amount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="n">Required</span><span class="p">]</span>
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">200</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">CustomerEmail</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">50</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Status</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">"Pending"</span><span class="p">;</span> <span class="c1">// Pending, Completed, Failed, Refunded</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">TransactionId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">50</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">PaymentMethod</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">"CreditCard"</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">;</span>
        <span class="k">public</span> <span class="n">DateTime</span><span class="p">?</span> <span class="n">ProcessedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">500</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">FailureReason</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="p">[</span><span class="nf">MaxLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">ExternalTransactionId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="datapaymentdbcontextcs"><strong>Data/PaymentDbContext.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">PaymentDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">PaymentDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
        <span class="p">{</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;</span> <span class="n">PaymentTransactions</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;(</span><span class="n">entity</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">OrderId</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">HasIndex</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">CustomerEmail</span><span class="p">);</span>
                <span class="n">entity</span><span class="p">.</span><span class="nf">Property</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">).</span><span class="nf">IsRequired</span><span class="p">();</span>
            <span class="p">});</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="servicespaymentprocessorcs"><strong>Services/PaymentProcessor.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IPaymentProcessor</span>
    <span class="p">{</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="n">PaymentResult</span><span class="p">&gt;</span> <span class="nf">ProcessPaymentAsync</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">,</span> <span class="kt">string</span> <span class="n">customerEmail</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentProcessor</span> <span class="p">:</span> <span class="n">IPaymentProcessor</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentProcessor</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">Random</span> <span class="n">_random</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Random</span><span class="p">();</span>

        <span class="k">public</span> <span class="nf">PaymentProcessor</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentProcessor</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">PaymentResult</span><span class="p">&gt;</span> <span class="nf">ProcessPaymentAsync</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">,</span> <span class="kt">string</span> <span class="n">customerEmail</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Processing payment of $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s"> for </span><span class="p">{</span><span class="n">customerEmail</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            
            <span class="c1">// Simulate payment processing time</span>
            <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">2</span><span class="p">));</span>

            <span class="c1">// Simulate payment success/failure (90% success rate for demo)</span>
            <span class="kt">var</span> <span class="n">isSuccess</span> <span class="p">=</span> <span class="n">_random</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">101</span><span class="p">)</span> <span class="p">&lt;=</span> <span class="m">90</span><span class="p">;</span>
            
            <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">new</span> <span class="n">PaymentResult</span>
            <span class="p">{</span>
                <span class="n">IsSuccess</span> <span class="p">=</span> <span class="n">isSuccess</span><span class="p">,</span>
                <span class="n">TransactionId</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="nf">NewGuid</span><span class="p">().</span><span class="nf">ToString</span><span class="p">(</span><span class="s">"N"</span><span class="p">)[..</span><span class="m">16</span><span class="p">].</span><span class="nf">ToUpper</span><span class="p">(),</span>
                <span class="n">Amount</span> <span class="p">=</span> <span class="n">amount</span><span class="p">,</span>
                <span class="n">ProcessedAt</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span>
            <span class="p">};</span>

            <span class="k">if</span> <span class="p">(!</span><span class="n">isSuccess</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">failureReasons</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
                <span class="p">{</span>
                    <span class="s">"Insufficient funds"</span><span class="p">,</span>
                    <span class="s">"Card expired"</span><span class="p">,</span>
                    <span class="s">"Card declined"</span><span class="p">,</span>
                    <span class="s">"Invalid card number"</span><span class="p">,</span>
                    <span class="s">"Processing error"</span>
                <span class="p">};</span>
                <span class="n">result</span><span class="p">.</span><span class="n">FailureReason</span> <span class="p">=</span> <span class="n">failureReasons</span><span class="p">[</span><span class="n">_random</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="n">failureReasons</span><span class="p">.</span><span class="n">Length</span><span class="p">)];</span>
            <span class="p">}</span>

            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Payment result: </span><span class="p">{(</span><span class="n">isSuccess</span> <span class="p">?</span> <span class="s">"Success"</span> <span class="p">:</span> <span class="s">"Failed"</span><span class="p">)}</span><span class="s"> - </span><span class="p">{</span><span class="n">result</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentResult</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">bool</span> <span class="n">IsSuccess</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">TransactionId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Amount</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="n">DateTime</span> <span class="n">ProcessedAt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">FailureReason</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="servicesinventoryreservedlistenercs"><strong>Services/InventoryReservedListener.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Shared.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API.Services</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">InventoryReservedListener</span> <span class="p">:</span> <span class="n">IHostedService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ISubscriber</span> <span class="n">_subscriber</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IPublisher</span> <span class="n">_publisher</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceScopeFactory</span> <span class="n">_scopeFactory</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryReservedListener</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">InventoryReservedListener</span><span class="p">(</span>
            <span class="n">ISubscriber</span> <span class="n">subscriber</span><span class="p">,</span>
            <span class="n">IPublisher</span> <span class="n">publisher</span><span class="p">,</span>
            <span class="n">IServiceScopeFactory</span> <span class="n">scopeFactory</span><span class="p">,</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">InventoryReservedListener</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span> <span class="p">=</span> <span class="n">subscriber</span><span class="p">;</span>
            <span class="n">_publisher</span> <span class="p">=</span> <span class="n">publisher</span><span class="p">;</span>
            <span class="n">_scopeFactory</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StartAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_subscriber</span><span class="p">.</span><span class="nf">Subscribe</span><span class="p">(</span><span class="n">ProcessInventoryReserved</span><span class="p">);</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Reserved Listener started"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kt">bool</span> <span class="nf">ProcessInventoryReserved</span><span class="p">(</span><span class="kt">string</span> <span class="n">message</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">&gt;</span> <span class="n">headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">PaymentProcessedEvent</span> <span class="n">response</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Received inventory reserved event: </span><span class="p">{</span><span class="n">message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">inventoryEvent</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">InventoryReservedEvent</span><span class="p">&gt;(</span><span class="n">message</span><span class="p">);</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">inventoryEvent</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="s">"Failed to deserialize inventory reserved event"</span><span class="p">);</span>
                    <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
                <span class="p">}</span>

                <span class="n">response</span> <span class="p">=</span> <span class="k">new</span> <span class="n">PaymentProcessedEvent</span>
                <span class="p">{</span>
                    <span class="n">OrderId</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">OrderId</span><span class="p">,</span>
                    <span class="n">ProductId</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                    <span class="n">Amount</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span>
                    <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">CustomerEmail</span>
                <span class="p">};</span>

                <span class="k">if</span> <span class="p">(!</span><span class="n">inventoryEvent</span><span class="p">.</span><span class="n">IsSuccess</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Inventory reservation failed, no payment needed</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">"Payment skipped due to inventory failure"</span><span class="p">;</span>
                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Skipping payment for order </span><span class="p">{</span><span class="n">inventoryEvent</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s"> due to inventory failure"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="c1">// Process payment</span>
                    <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
                    <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">PaymentDbContext</span><span class="p">&gt;();</span>
                    <span class="kt">var</span> <span class="n">paymentProcessor</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">IPaymentProcessor</span><span class="p">&gt;();</span>

                    <span class="c1">// Create payment record</span>
                    <span class="kt">var</span> <span class="n">payment</span> <span class="p">=</span> <span class="k">new</span> <span class="n">PaymentTransaction</span>
                    <span class="p">{</span>
                        <span class="n">OrderId</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">OrderId</span><span class="p">,</span>
                        <span class="n">ProductId</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">ProductId</span><span class="p">,</span>
                        <span class="n">Amount</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span>
                        <span class="n">CustomerEmail</span> <span class="p">=</span> <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">CustomerEmail</span><span class="p">,</span>
                        <span class="n">Status</span> <span class="p">=</span> <span class="s">"Processing"</span>
                    <span class="p">};</span>

                    <span class="n">context</span><span class="p">.</span><span class="n">PaymentTransactions</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">payment</span><span class="p">);</span>
                    <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>

                    <span class="c1">// Process payment</span>
                    <span class="kt">var</span> <span class="n">paymentResult</span> <span class="p">=</span> <span class="n">paymentProcessor</span><span class="p">.</span><span class="nf">ProcessPaymentAsync</span><span class="p">(</span>
                        <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">TotalAmount</span><span class="p">,</span> 
                        <span class="n">inventoryEvent</span><span class="p">.</span><span class="n">CustomerEmail</span><span class="p">).</span><span class="n">Result</span><span class="p">;</span>

                    <span class="c1">// Update payment record</span>
                    <span class="n">payment</span><span class="p">.</span><span class="n">Status</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">?</span> <span class="s">"Completed"</span> <span class="p">:</span> <span class="s">"Failed"</span><span class="p">;</span>
                    <span class="n">payment</span><span class="p">.</span><span class="n">TransactionId</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">;</span>
                    <span class="n">payment</span><span class="p">.</span><span class="n">ProcessedAt</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">ProcessedAt</span><span class="p">;</span>
                    <span class="n">payment</span><span class="p">.</span><span class="n">FailureReason</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">FailureReason</span><span class="p">;</span>
                    <span class="n">payment</span><span class="p">.</span><span class="n">ExternalTransactionId</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">;</span>

                    <span class="n">context</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>

                    <span class="c1">// Prepare response</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">IsSuccess</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">TransactionId</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">TransactionId</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="n">paymentResult</span><span class="p">.</span><span class="n">IsSuccess</span> 
                        <span class="p">?</span> <span class="s">"Payment processed successfully"</span> 
                        <span class="p">:</span> <span class="s">$"Payment failed: </span><span class="p">{</span><span class="n">paymentResult</span><span class="p">.</span><span class="n">FailureReason</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>

                    <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Payment processing completed for order </span><span class="p">{</span><span class="n">inventoryEvent</span><span class="p">.</span><span class="n">OrderId</span><span class="p">}</span><span class="s">: "</span> <span class="p">+</span>
                                         <span class="s">$"</span><span class="p">{(</span><span class="n">paymentResult</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">?</span> <span class="s">"Success"</span> <span class="p">:</span> <span class="s">"Failed"</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Publish payment response</span>
                <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                    <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">response</span><span class="p">),</span>
                    <span class="s">"payment.processed"</span><span class="p">,</span>
                    <span class="k">null</span><span class="p">);</span>

                <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span><span class="n">ex</span><span class="p">,</span> <span class="s">"Error processing inventory reserved event"</span><span class="p">);</span>
                
                <span class="c1">// Publish failure response</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">response</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">IsSuccess</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
                    <span class="n">response</span><span class="p">.</span><span class="n">Message</span> <span class="p">=</span> <span class="s">"Internal error processing payment"</span><span class="p">;</span>
                    
                    <span class="n">_publisher</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span>
                        <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">response</span><span class="p">),</span>
                        <span class="s">"payment.processed"</span><span class="p">,</span>
                        <span class="k">null</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">Task</span> <span class="nf">StopAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Inventory Reserved Listener stopped"</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="controllerspaymentscontrollercs"><strong>Controllers/PaymentsController.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Models</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">PaymentsController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">PaymentDbContext</span> <span class="n">_context</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentsController</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">PaymentsController</span><span class="p">(</span><span class="n">PaymentDbContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PaymentsController</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetPayments</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">PaymentTransactions</span>
                <span class="p">.</span><span class="nf">OrderByDescending</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">CreatedAt</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;&gt;</span> <span class="nf">GetPayment</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">payment</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">PaymentTransactions</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">payment</span> <span class="p">==</span> <span class="k">null</span> <span class="p">?</span> <span class="nf">NotFound</span><span class="p">()</span> <span class="p">:</span> <span class="n">payment</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"order/{orderId}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;&gt;</span> <span class="nf">GetPaymentByOrder</span><span class="p">(</span><span class="kt">int</span> <span class="n">orderId</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">payment</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">PaymentTransactions</span>
                <span class="p">.</span><span class="nf">FirstOrDefaultAsync</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">OrderId</span> <span class="p">==</span> <span class="n">orderId</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">payment</span> <span class="p">==</span> <span class="k">null</span> <span class="p">?</span> <span class="nf">NotFound</span><span class="p">()</span> <span class="p">:</span> <span class="n">payment</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"transaction/{transactionId}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">PaymentTransaction</span><span class="p">&gt;&gt;</span> <span class="nf">GetPaymentByTransaction</span><span class="p">(</span><span class="kt">string</span> <span class="n">transactionId</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">payment</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">PaymentTransactions</span>
                <span class="p">.</span><span class="nf">FirstOrDefaultAsync</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">TransactionId</span> <span class="p">==</span> <span class="n">transactionId</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">payment</span> <span class="p">==</span> <span class="k">null</span> <span class="p">?</span> <span class="nf">NotFound</span><span class="p">()</span> <span class="p">:</span> <span class="n">payment</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="appsettingsjson-2"><strong>appsettings.json</strong></h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.AspNetCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.EntityFrameworkCore"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DefaultConnection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data Source=payments.db"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"PaymentSettings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"SuccessRate"</span><span class="p">:</span><span class="w"> </span><span class="mi">90</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ProcessingDelayMs"</span><span class="p">:</span><span class="w"> </span><span class="mi">2000</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"AllowedHosts"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="programcs-2"><strong>Program.cs</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Payment.API.Services</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Plain.RabbitMQ</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">RabbitMQ.Client</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Payment.API</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

            <span class="c1">// Add services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">();</span>

            <span class="c1">// Database</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">PaymentDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
                <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlite</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

            <span class="c1">// Payment Service</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IPaymentProcessor</span><span class="p">,</span> <span class="n">PaymentProcessor</span><span class="p">&gt;();</span>

            <span class="c1">// RabbitMQ Configuration</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(</span>
                <span class="k">new</span> <span class="nf">ConnectionProvider</span><span class="p">(</span><span class="s">"amqp://guest:guest@localhost:5672"</span><span class="p">));</span>

            <span class="c1">// Publisher for Payment events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">IPublisher</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Publisher</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"payment.exchange"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Subscriber for Inventory events</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ISubscriber</span><span class="p">&gt;(</span><span class="n">provider</span> <span class="p">=&gt;</span>
                <span class="k">new</span> <span class="nf">Subscriber</span><span class="p">(</span>
                    <span class="n">provider</span><span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IConnectionProvider</span><span class="p">&gt;(),</span>
                    <span class="s">"inventory.exchange"</span><span class="p">,</span>
                    <span class="s">"inventory.reserved.queue"</span><span class="p">,</span>
                    <span class="s">"inventory.reserved"</span><span class="p">,</span>
                    <span class="n">ExchangeType</span><span class="p">.</span><span class="n">Topic</span><span class="p">));</span>

            <span class="c1">// Background Services</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">InventoryReservedListener</span><span class="p">&gt;();</span>

            <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

            <span class="c1">// Configure the HTTP request pipeline</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
                <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

            <span class="c1">// Ensure database is created</span>
            <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">PaymentDbContext</span><span class="p">&gt;();</span>
                <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Run <code class="language-plaintext highlighter-rouge">dotnet build</code> to ensure everything compiles successfully.</li>
</ul>

<h2 id="step-7-configure-multiple-startup-projects"><strong>Step 7: Configure Multiple Startup Projects</strong></h2>

<ol>
  <li>In Visual Studio, right-click solution → <strong>“Set StartUp Projects”</strong></li>
  <li>Select <strong>“Multiple startup projects”</strong></li>
  <li>Set <strong>Order.API</strong>, <strong>Catalog.API</strong>, and <strong>Payment.API</strong> to <strong>“Start”</strong></li>
  <li>Configure different ports in <code class="language-plaintext highlighter-rouge">launchSettings.json</code>:
    <ul>
      <li>Order.API: <code class="language-plaintext highlighter-rouge">https://localhost:7001</code></li>
      <li>Catalog.API: <code class="language-plaintext highlighter-rouge">https://localhost:7002</code></li>
      <li>Payment.API: <code class="language-plaintext highlighter-rouge">https://localhost:7003</code></li>
    </ul>
  </li>
</ol>

<h2 id="step-8-testing-the-enhanced-saga-implementation"><strong>Step 8: Testing the Enhanced SAGA Implementation</strong></h2>

<h3 id="test-scenario-1-complete-success-flow-happy-path"><strong>Test Scenario 1: Complete Success Flow (Happy Path)</strong></h3>

<ol>
  <li><strong>Check Products</strong>: Open Catalog.API Swagger at <code class="language-plaintext highlighter-rouge">https://localhost:7002/swagger</code>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /api/products
</code></pre></div>    </div>
  </li>
  <li><strong>Create Order</strong>: Open Order.API Swagger at <code class="language-plaintext highlighter-rouge">https://localhost:7001/swagger</code>
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST</span><span class="w"> </span><span class="err">/api/orders</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"productId"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"productName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Gaming Laptop"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"unitPrice"</span><span class="p">:</span><span class="w"> </span><span class="mf">1299.99</span><span class="p">,</span><span class="w">
  </span><span class="nl">"customerEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"customer@example.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><strong>Expected Flow</strong>:
    <ul>
      <li>Order status: Pending → InventoryReserved → PaymentProcessing → Confirmed</li>
      <li>Inventory: Available stock decreases, reserved stock increases</li>
      <li>Payment: Transaction created and processed successfully</li>
    </ul>
  </li>
  <li><strong>Verify Results</strong>:
    <ul>
      <li>Order status should be “Confirmed”</li>
      <li>Product inventory properly updated</li>
      <li>Payment transaction completed in Payment.API</li>
    </ul>
  </li>
</ol>

<h3 id="test-scenario-2-inventory-failure-immediate-compensation"><strong>Test Scenario 2: Inventory Failure (Immediate Compensation)</strong></h3>

<ol>
  <li><strong>Create Large Order</strong>:
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST</span><span class="w"> </span><span class="err">/api/orders</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"productId"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"productName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Gaming Laptop"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w">
  </span><span class="nl">"unitPrice"</span><span class="p">:</span><span class="w"> </span><span class="mf">1299.99</span><span class="p">,</span><span class="w">
  </span><span class="nl">"customerEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"customer@example.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><strong>Expected Flow</strong>:
    <ul>
      <li>Order status: Pending → Cancelled</li>
      <li>No inventory changes</li>
      <li>No payment attempted</li>
    </ul>
  </li>
</ol>

<h3 id="test-scenario-3-payment-failure-multi-step-compensation"><strong>Test Scenario 3: Payment Failure (Multi-step Compensation)</strong></h3>

<ol>
  <li><strong>Create Valid Order</strong> (payment service has ~10% failure rate for demo):
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST</span><span class="w"> </span><span class="err">/api/orders</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"productId"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"productName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Gaming Laptop"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"unitPrice"</span><span class="p">:</span><span class="w"> </span><span class="mf">1299.99</span><span class="p">,</span><span class="w">
  </span><span class="nl">"customerEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"customer@example.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><strong>If Payment Fails, Observe</strong>:
    <ul>
      <li>Order status: Pending → InventoryReserved → Cancelled</li>
      <li>Reserved inventory released back to available stock</li>
      <li>Payment transaction marked as failed</li>
    </ul>
  </li>
</ol>

<h3 id="test-scenario-4-non-existent-product"><strong>Test Scenario 4: Non-existent Product</strong></h3>

<ol>
  <li><strong>Create Invalid Order</strong>:
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST</span><span class="w"> </span><span class="err">/api/orders</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"productId"</span><span class="p">:</span><span class="w"> </span><span class="mi">999</span><span class="p">,</span><span class="w">
  </span><span class="nl">"productName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Non-existent Product"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
  </span><span class="nl">"unitPrice"</span><span class="p">:</span><span class="w"> </span><span class="mf">100.00</span><span class="p">,</span><span class="w">
  </span><span class="nl">"customerEmail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"customer@example.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><strong>Expected Results</strong>:
    <ul>
      <li>Order cancelled immediately</li>
      <li>Error message: “Product with ID 999 not found”</li>
    </ul>
  </li>
</ol>

<h3 id="monitor-the-complete-flow"><strong>Monitor the Complete Flow</strong></h3>

<p><strong>Using RabbitMQ Management UI:</strong></p>
<ol>
  <li>Open <code class="language-plaintext highlighter-rouge">http://localhost:15672</code></li>
  <li>Navigate to <strong>Exchanges</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">order.exchange</code> - Order events</li>
      <li><code class="language-plaintext highlighter-rouge">inventory.exchange</code> - Inventory events</li>
      <li><code class="language-plaintext highlighter-rouge">payment.exchange</code> - Payment events</li>
    </ul>
  </li>
  <li>Check <strong>Queues</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">order.created.queue</code></li>
      <li><code class="language-plaintext highlighter-rouge">inventory.response.queue</code></li>
      <li><code class="language-plaintext highlighter-rouge">payment.response.queue</code></li>
      <li><code class="language-plaintext highlighter-rouge">inventory.release.queue</code></li>
    </ul>
  </li>
</ol>

<p><strong>Service Logs Monitoring:</strong>
Watch the console logs of all three services to see:</p>
<ul>
  <li>Event publishing and receiving</li>
  <li>Business logic processing</li>
  <li>Compensation actions</li>
  <li>Error handling</li>
</ul>

<p><strong>APIs for Verification:</strong></p>
<ul>
  <li><strong>Orders</strong>: <code class="language-plaintext highlighter-rouge">GET https://localhost:7001/api/orders</code></li>
  <li><strong>Products</strong>: <code class="language-plaintext highlighter-rouge">GET https://localhost:7002/api/products</code></li>
  <li><strong>Payments</strong>: <code class="language-plaintext highlighter-rouge">GET https://localhost:7003/api/payments</code></li>
</ul>

<hr />

<h2 id="step-9-understanding-the-enhanced-saga-flow"><strong>Step 9: Understanding the Enhanced SAGA Flow</strong></h2>

<h3 id="complete-success-flow"><strong>Complete Success Flow:</strong></h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Customer creates order via Order.API
2. Order.API saves order with "Pending" status
3. Order.API publishes OrderCreatedEvent to RabbitMQ
4. Catalog.API receives OrderCreatedEvent
5. Catalog.API reserves inventory and publishes InventoryReservedEvent
6. Order.API updates order status to "InventoryReserved"
7. Payment.API receives InventoryReservedEvent
8. Payment.API processes payment and publishes PaymentProcessedEvent
9. Order.API receives PaymentProcessedEvent (success) and updates to "Confirmed"
10. Catalog.API receives PaymentProcessedEvent (inventory reservation committed)
</code></pre></div></div>

<h3 id="compensation-flow-inventory-failure"><strong>Compensation Flow (Inventory Failure):</strong></h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Customer creates order via Order.API
2. Order.API saves order with "Pending" status
3. Order.API publishes OrderCreatedEvent to RabbitMQ
4. Catalog.API receives OrderCreatedEvent
5. Catalog.API detects insufficient stock
6. Catalog.API publishes InventoryReservedEvent (success=false)
7. Order.API receives InventoryReservedEvent and updates to "Cancelled"
</code></pre></div></div>

<h3 id="compensation-flow-payment-failure"><strong>Compensation Flow (Payment Failure):</strong></h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1-6. Same as success flow through inventory reservation
7. Payment.API processes payment and fails
8. Payment.API publishes PaymentProcessedEvent (success=false)
9. Order.API receives PaymentProcessedEvent and updates to "Cancelled"
10. Order.API publishes InventoryReleaseEvent for compensation
11. Catalog.API receives InventoryReleaseEvent and releases reserved inventory
</code></pre></div></div>

<h3 id="enhanced-business-logic-features"><strong>Enhanced Business Logic Features:</strong></h3>

<p><strong>Order Status Tracking:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Pending</code> - Order created, awaiting inventory check</li>
  <li><code class="language-plaintext highlighter-rouge">InventoryReserved</code> - Inventory allocated, awaiting payment</li>
  <li><code class="language-plaintext highlighter-rouge">PaymentProcessing</code> - Payment being processed</li>
  <li><code class="language-plaintext highlighter-rouge">Confirmed</code> - All steps successful</li>
  <li><code class="language-plaintext highlighter-rouge">Cancelled</code> - Failed at any step</li>
</ul>

<p><strong>Inventory Management:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">AvailableStock</code> - Immediately available for new orders</li>
  <li><code class="language-plaintext highlighter-rouge">ReservedStock</code> - Allocated to pending orders awaiting payment</li>
  <li>Automatic release of reservations when payments fail</li>
</ul>

<p><strong>Payment Processing:</strong></p>
<ul>
  <li>Realistic payment simulation with configurable failure rates</li>
  <li>Transaction tracking with external transaction IDs</li>
  <li>Detailed failure reason tracking</li>
</ul>

<hr />

<h2 id="production-considerations"><strong>Production Considerations</strong></h2>

<h3 id="error-handling-and-resilience"><strong>Error Handling and Resilience</strong></h3>
<ul>
  <li><strong>Retry Logic</strong>: Implement exponential backoff for failed messages</li>
  <li><strong>Dead Letter Queues</strong>: Handle messages that fail repeatedly</li>
  <li><strong>Idempotency</strong>: Ensure message processing is idempotent</li>
  <li><strong>Timeouts</strong>: Implement timeouts for long-running processes</li>
</ul>

<h3 id="monitoring-and-observability"><strong>Monitoring and Observability</strong></h3>
<ul>
  <li><strong>Correlation IDs</strong>: Track requests across services</li>
  <li><strong>Structured Logging</strong>: Use structured logging for better searchability</li>
  <li><strong>Health Checks</strong>: Implement health checks for all services</li>
  <li><strong>Metrics</strong>: Monitor message processing rates and errors</li>
</ul>

<h3 id="data-consistency"><strong>Data Consistency</strong></h3>
<ul>
  <li><strong>Event Sourcing</strong>: Consider event sourcing for audit trails</li>
  <li><strong>Outbox Pattern</strong>: Ensure reliable message publishing</li>
  <li><strong>Saga State Management</strong>: Track saga state for complex workflows</li>
</ul>

<p>Hopefully, this enhanced implementation provides a robust foundation for distributed transactions using the SAGA pattern with RabbitMQ and ASP.NET Core. If you have any questions or need further assistance, feel free to ask in the comments! If you found this guide helpful, please share it with your network. Happy coding!</p>

<hr />

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/main/saga-choreography-v02">Complete Source Code on GitHub</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term="ASP.NET Core" /><category term="Microservices" /><category term="RabbitMQ" /><category term="Design Pattern" /><category term="aspnetcore" /><category term="microservices" /><category term="rabbitmq" /><category term="designpattern" /><category term="saga" /><category term="distributedtransactions" /><category term="choreography" /><category term="dataconsistency" /><category term="eventdriven" /><summary type="html"><![CDATA[Learn how to implement distributed transactions in microservices using the SAGA choreography pattern with RabbitMQ and ASP.NET Core. This comprehensive guide covers data consistency challenges, event-driven architecture, and step-by-step implementation with practical code examples.]]></summary></entry><entry><title type="html">Middleware in ASP.NET Core: Why It Matters and How to Build Custom Middleware with a Real-World Examples</title><link href="https://mahedee.net/middleware-in-aspnet-core-why-it-matters-and-how-to-build-custom-middleware/" rel="alternate" type="text/html" title="Middleware in ASP.NET Core: Why It Matters and How to Build Custom Middleware with a Real-World Examples" /><published>2026-02-06T00:00:00-05:00</published><updated>2026-02-06T00:00:00-05:00</updated><id>https://mahedee.net/middleware-in-aspnet-core-why-it-matters-and-how-to-build-custom-middleware</id><content type="html" xml:base="https://mahedee.net/middleware-in-aspnet-core-why-it-matters-and-how-to-build-custom-middleware/"><![CDATA[<p>Modern web applications must handle cross-cutting concerns such as logging, authentication, security, exception handling, and request tracing. In <strong>ASP.NET Core</strong>, middleware is the key architectural component that makes all of this possible in a clean, modular, and scalable way.</p>

<p>This article explains <strong>what middleware is</strong>, <strong>why it is important</strong>, <strong>its benefits</strong>, and <strong>how custom middleware is used in real-world and enterprise applications</strong>, with practical examples.</p>

<p><strong>What Is Middleware in ASP.NET Core?</strong></p>

<p>Middleware in ASP.NET Core is a software component that sits between the incoming HTTP request and the outgoing HTTP response. Each middleware component:</p>

<ul>
  <li>Receives an HTTP request</li>
  <li>Can perform some logic</li>
  <li>Can pass the request to the next middleware</li>
  <li>Can perform logic again when the response is returned</li>
</ul>

<p><strong>Request Pipeline Concept</strong></p>

<p><img src="/assets/images/posts/2026/middleware-chain-pattern.png" alt="" /></p>

<p>Each middleware decides:</p>

<ul>
  <li>Whether to pass the request forward</li>
  <li>Whether to short-circuit the pipeline</li>
</ul>

<p><strong>Benefits of Middleware in ASP.NET Core</strong></p>

<p>Middleware provides several important benefits:</p>

<ul>
  <li>
    <p><strong>Separation of Concerns</strong>
Cross-cutting concerns (logging, security, error handling) are separated from business logic.</p>
  </li>
  <li>
    <p><strong>Reusability</strong>
Middleware components can be reused across multiple applications. For example, authentication middleware can be shared.</p>
  </li>
  <li>
    <p><strong>Centralized Control</strong>
Common logic like authentication, headers, or exception handling is implemented in one place.</p>
  </li>
  <li>
    <p><strong>Ordered Execution</strong>
Middleware runs in a defined order, giving precise control over request handling.</p>
  </li>
  <li>
    <p><strong>Performance Optimization</strong>
Requests can be terminated early (for example, unauthorized requests), saving resources.</p>
  </li>
</ul>

<p><strong>Why Middleware Is Important?</strong></p>

<p>Without middleware:</p>

<ul>
  <li>Logging code would be duplicated everywhere</li>
  <li>Security logic would pollute controllers</li>
  <li>Error handling would be inconsistent</li>
  <li>Applications would become hard to maintain</li>
</ul>

<p>Middleware enables:</p>

<ul>
  <li>Clean architecture</li>
  <li>Maintainable code</li>
  <li>Enterprise-grade scalability</li>
  <li>Observability and diagnostics</li>
</ul>

<p>In modern APIs and microservices, middleware is <strong>not optional</strong>—it is foundational.</p>

<p><strong>Built-in Middleware Components in ASP.NET Core</strong></p>

<p>ASP.NET Core provides many built-in middleware components.</p>

<p><strong>1. Exception Handling Middleware</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseExceptionHandler</span><span class="p">(</span><span class="s">"/error"</span><span class="p">);</span>
</code></pre></div></div>
<p>Used to handle unhandled exceptions globally. Here /error is the endpoint that returns a standardized error response.
Real-world use: Enterprise applications use centralized error handling to return consistent error responses and log incidents.</p>

<p><strong>2. Authentication and Authorization</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
</code></pre></div></div>

<p>Real-world use: Validates JWT tokens or OAuth credentials before requests reach controllers.</p>

<p><strong>3. Logging Middleware</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseHttpLogging</span><span class="p">();</span>
</code></pre></div></div>

<p>Real-world use: Tracks incoming requests and outgoing responses for auditing and debugging.</p>

<p><strong>4. CORS Middleware</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseCors</span><span class="p">();</span>
</code></pre></div></div>
<p>Real-world use: Allows controlled access to APIs from frontend applications.</p>

<p><strong>What Is Custom Middleware in ASP.NET Core?</strong></p>

<p><strong>Custom middleware</strong> is middleware that you write yourself to handle application-specific or domain-specific requirements.</p>

<p>You create custom middleware when:</p>

<ul>
  <li>Built-in middleware does not meet your needs</li>
  <li>You want reusable cross-cutting logic</li>
  <li>You need centralized enterprise behavior</li>
</ul>

<p><strong>Why Use Custom Middleware?</strong></p>

<p>ASP.NET Core’s built-in middleware covers many common scenarios, but custom middleware allows you to address specific needs unique to your application or organization.<br />
Custom middleware provides several advantages:</p>

<ul>
  <li>Eliminates duplicated logic</li>
  <li>Enforces enterprise standards</li>
  <li>Improves security and observability</li>
  <li>Simplifies controllers</li>
  <li>Enables consistent behavior across APIs</li>
</ul>

<p><strong>How to Create Custom Middleware</strong></p>

<p><strong>Basic Custom Middleware Example</strong></p>

<p>Here RequestLoggingMiddleware logs request paths and response statuses. _next is a delegate to the next middleware in the pipeline which is invoked after logging the request. InvokeAsync is the method called for each HTTP request which performs the logging and calls the next middleware. This method is called automatically for each HTTP request.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">public</span> <span class="k">class</span> <span class="nc">RequestLoggingMiddleware</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">RequestLoggingMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Request Path: </span><span class="p">{</span><span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Path</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

        <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>

        <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Response Status: </span><span class="p">{</span><span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p><strong>Registering Middleware</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="n">UseMiddleware</span><span class="p">&lt;</span><span class="n">RequestLoggingMiddleware</span><span class="p">&gt;();</span>
</code></pre></div></div>

<!-- Completed up -->

<p><strong>Real-World Custom Middleware Examples</strong></p>

<p><strong>1. Correlation ID Middleware (Enterprise Tracing)</strong></p>

<p>Used in distributed systems to trace requests across services.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CorrelationIdMiddleware</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">HeaderName</span> <span class="p">=</span> <span class="s">"X-Correlation-ID"</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">CorrelationIdMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">HeaderName</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">correlationId</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="n">correlationId</span> <span class="p">=</span> <span class="n">Guid</span><span class="p">.</span><span class="nf">NewGuid</span><span class="p">().</span><span class="nf">ToString</span><span class="p">();</span>
            <span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Headers</span><span class="p">[</span><span class="n">HeaderName</span><span class="p">]</span> <span class="p">=</span> <span class="n">correlationId</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">Headers</span><span class="p">[</span><span class="n">HeaderName</span><span class="p">]</span> <span class="p">=</span> <span class="n">correlationId</span><span class="p">;</span>
        <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Enterprise use:</strong>
Used in microservices with tools like Seq, ELK, Application Insights.</p>

<p><strong>2. API Key Validation Middleware</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ApiKeyMiddleware</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">ApiKeyHeader</span> <span class="p">=</span> <span class="s">"X-API-KEY"</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">ApiKeyMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">ContainsKey</span><span class="p">(</span><span class="n">ApiKeyHeader</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">StatusCode</span> <span class="p">=</span> <span class="n">StatusCodes</span><span class="p">.</span><span class="n">Status401Unauthorized</span><span class="p">;</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Real-world use:</strong>
Used in B2B integrations where OAuth is unnecessary.</p>

<p><strong>3. Tenant Resolution Middleware (Multi-Tenant SaaS)</strong></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">TenantMiddleware</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">TenantMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">tenantId</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Headers</span><span class="p">[</span><span class="s">"X-Tenant-ID"</span><span class="p">];</span>
        <span class="n">context</span><span class="p">.</span><span class="n">Items</span><span class="p">[</span><span class="s">"TenantId"</span><span class="p">]</span> <span class="p">=</span> <span class="n">tenantId</span><span class="p">;</span>

        <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Enterprise use:</strong>
Multi-tenant SaaS platforms where each request belongs to a specific tenant.</p>

<p><strong>Create a real-world ASP.NET Core application to implement custom middleware</strong></p>

<p><strong>Use cases:</strong>
In this section, we will walk through creating a custom middleware that logs request and response details, including execution time.</p>

<p><strong>Step 1: Create an ASP.NET Core Project</strong></p>
<ul>
  <li>Open visual studio or your preferred IDE.</li>
  <li>Create a new ASP.NET Core Web API project name “MiddlewareExample”.</li>
  <li>Or, use the .NET CLI to create the project and add project to a solution file:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new webapi <span class="nt">-n</span> MiddlewareExample
<span class="nb">cd </span>MiddlewareExample
dotnet new sln <span class="nt">-n</span> MiddlewareSolution
dotnet sln MiddlewareSolution.sln add MiddlewareExample.csproj
</code></pre></div></div>

<p><strong>Step 2: Implement Custom Middleware</strong></p>
<ul>
  <li>Create a new folder named “Middleware” in the project.</li>
  <li>Inside the “Middleware” folder, create a new class file named “RequestResponseLoggingMiddleware.cs” and add the following code:</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Diagnostics</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">MiddlewareExample.Middleware</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">RequestResponseLoggingMiddleware</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">RequestDelegate</span> <span class="n">_next</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">RequestResponseLoggingMiddleware</span><span class="p">(</span><span class="n">RequestDelegate</span> <span class="n">next</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">InvokeAsync</span><span class="p">(</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">stopwatch</span> <span class="p">=</span> <span class="n">Stopwatch</span><span class="p">.</span><span class="nf">StartNew</span><span class="p">();</span>

            <span class="c1">// Log Request details</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Incoming Request: </span><span class="p">{</span><span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Method</span><span class="p">}</span><span class="s"> </span><span class="p">{</span><span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Path</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>

            <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>

            <span class="n">stopwatch</span><span class="p">.</span><span class="nf">Stop</span><span class="p">();</span>

            <span class="c1">// Log Response details</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Outgoing Response: </span><span class="p">{</span><span class="n">context</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">}</span><span class="s"> | Time Taken: </span><span class="p">{</span><span class="n">stopwatch</span><span class="p">.</span><span class="n">ElapsedMilliseconds</span><span class="p">}</span><span class="s"> ms"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p><strong>Step 3: Register Middleware in the Pipeline</strong></p>
<ul>
  <li>Open the “Program.cs” file and register the custom middleware in the HTTP request pipeline by adding the following line before <code class="language-plaintext highlighter-rouge">app.UseEndpoints(...)</code>:</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="n">UseMiddleware</span><span class="p">&lt;</span><span class="n">RequestResponseLoggingMiddleware</span><span class="p">&gt;();</span>
</code></pre></div></div>

<p>My final “Program.cs” should look like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">MiddlewareExample.Middleware</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Scalar.AspNetCore</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Add services to the container.</span>
<span class="c1">// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenApi</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Add the middleware to the HTTP request pipeline</span>
<span class="n">app</span><span class="p">.</span><span class="n">UseMiddleware</span><span class="p">&lt;</span><span class="n">RequestResponseLoggingMiddleware</span><span class="p">&gt;();</span>

<span class="c1">// Configure the HTTP request pipeline.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">MapOpenApi</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">MapScalarApiReference</span><span class="p">();</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">summaries</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
<span class="p">{</span>
    <span class="s">"Freezing"</span><span class="p">,</span> <span class="s">"Bracing"</span><span class="p">,</span> <span class="s">"Chilly"</span><span class="p">,</span> <span class="s">"Cool"</span><span class="p">,</span> <span class="s">"Mild"</span><span class="p">,</span> <span class="s">"Warm"</span><span class="p">,</span> <span class="s">"Balmy"</span><span class="p">,</span> <span class="s">"Hot"</span><span class="p">,</span> <span class="s">"Sweltering"</span><span class="p">,</span> <span class="s">"Scorching"</span>
<span class="p">};</span>

<span class="n">app</span><span class="p">.</span><span class="nf">MapGet</span><span class="p">(</span><span class="s">"/weatherforecast"</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">forecast</span> <span class="p">=</span>  <span class="n">Enumerable</span><span class="p">.</span><span class="nf">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">5</span><span class="p">).</span><span class="nf">Select</span><span class="p">(</span><span class="n">index</span> <span class="p">=&gt;</span>
        <span class="k">new</span> <span class="nf">WeatherForecast</span>
        <span class="p">(</span>
            <span class="n">DateOnly</span><span class="p">.</span><span class="nf">FromDateTime</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="nf">AddDays</span><span class="p">(</span><span class="n">index</span><span class="p">)),</span>
            <span class="n">Random</span><span class="p">.</span><span class="n">Shared</span><span class="p">.</span><span class="nf">Next</span><span class="p">(-</span><span class="m">20</span><span class="p">,</span> <span class="m">55</span><span class="p">),</span>
            <span class="n">summaries</span><span class="p">[</span><span class="n">Random</span><span class="p">.</span><span class="n">Shared</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="n">summaries</span><span class="p">.</span><span class="n">Length</span><span class="p">)]</span>
        <span class="p">))</span>
        <span class="p">.</span><span class="nf">ToArray</span><span class="p">();</span>
    <span class="k">return</span> <span class="n">forecast</span><span class="p">;</span>
<span class="p">})</span>
<span class="p">.</span><span class="nf">WithName</span><span class="p">(</span><span class="s">"GetWeatherForecast"</span><span class="p">);</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>

<span class="k">record</span> <span class="nc">WeatherForecast</span><span class="p">(</span><span class="n">DateOnly</span> <span class="n">Date</span><span class="p">,</span> <span class="kt">int</span> <span class="n">TemperatureC</span><span class="p">,</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Summary</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">TemperatureF</span> <span class="p">=&gt;</span> <span class="m">32</span> <span class="p">+</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">TemperatureC</span> <span class="p">/</span> <span class="m">0.5556</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Step 4: Run the Application</strong></p>
<ul>
  <li>Run the application using Visual Studio or the .NET CLI:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet run
</code></pre></div></div>
<p><strong>Step 5: Test the Middleware</strong></p>
<ul>
  <li>Use a tool like Postman or curl to send requests to the API endpoints. For example, browse to <code class="language-plaintext highlighter-rouge">http://localhost:5113/weatherforecast</code> or any other endpoint defined in your API.</li>
  <li>Observe the console output to see the logged request and response details along with the execution time.</li>
  <li>Sample output in the console:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Outgoing Response: 404 | Time Taken: 10 ms
Incoming Request: GET /swagger
Outgoing Response: 404 | Time Taken: 0 ms
Incoming Request: GET /scalar/v1
Outgoing Response: 200 | Time Taken: 16 ms
Incoming Request: GET /scalar/scalar.js
Outgoing Response: 200 | Time Taken: 30 ms
Incoming Request: GET /openapi/v1.json
Outgoing Response: 200 | Time Taken: 96 ms
Incoming Request: GET /weatherforecast
Outgoing Response: 200 | Time Taken: 13 ms
Incoming Request: GET /favicon.ico
Outgoing Response: 404 | Time Taken: 0 ms
</code></pre></div>    </div>
  </li>
</ul>

<p><strong>Understanding Middleware Execution Order</strong>
The order in which middleware components are added to the pipeline matters. Middleware is executed in the order it is registered. For example, if you have authentication middleware before logging middleware, the logging middleware will only log requests that have passed authentication.</p>

<p>Here is an example of middleware registration order:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span> <span class="c1">// Authentication middleware</span>
<span class="n">app</span><span class="p">.</span><span class="n">UseMiddleware</span><span class="p">&lt;</span><span class="n">RequestResponseLoggingMiddleware</span><span class="p">&gt;();</span> <span class="c1">// Custom logging middleware</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span> <span class="c1">// Authorization middleware</span>
</code></pre></div></div>

<p><strong>Conclusion</strong>
Middleware is a powerful feature of ASP.NET Core that enables developers to handle cross-cutting concerns in a clean and modular way. By leveraging both built-in and custom middleware, you can create scalable, maintainable, and enterprise-grade web applications.</p>

<hr />
<p><a href="https://github.com/mahedee/code-sample02/tree/main/MiddlewareExample">Source Code on GitHub</a></p>

<p>Feel free to share your thoughts or questions in the comments below! Please share this article if you found it helpful. Happy coding!</p>]]></content><author><name>Mahedee Hasan</name></author><category term="ASP.NET Core" /><category term=".NET Core" /><category term="Software Architecture" /><category term="Web API" /><category term="aspnetcore" /><category term="dotnet" /><category term="middleware" /><category term="webapi" /><category term="csharp" /><category term="software-architecture" /><category term="programming" /><category term="customization" /><summary type="html"><![CDATA[Learn why middleware matters in ASP.NET Core and how to build custom middleware with practical, real-world examples used in modern applications.]]></summary></entry><entry><title type="html">Factory Design Pattern Explained with 10 Real-World Use Cases</title><link href="https://mahedee.net/factory-design-pattern-explained-with-10-real-world-use-cases/" rel="alternate" type="text/html" title="Factory Design Pattern Explained with 10 Real-World Use Cases" /><published>2026-01-15T00:00:00-05:00</published><updated>2026-01-15T00:00:00-05:00</updated><id>https://mahedee.net/factory-design-pattern-explained-with-10-real-world-use-cases</id><content type="html" xml:base="https://mahedee.net/factory-design-pattern-explained-with-10-real-world-use-cases/"><![CDATA[<p>The <strong>Factory Design Pattern</strong> is one of the most widely used <strong>creational design patterns</strong> in real-world software systems. It helps create objects <strong>without exposing the instantiation logic</strong> to the client and promotes <strong>loose coupling</strong>, <strong>scalability</strong>, and <strong>maintainability</strong>.</p>

<p>This article explores <strong>10 real-world use cases of the Factory Pattern</strong>, explaining the <strong>problem</strong>, <strong>solution</strong>, <strong>key components</strong>, <strong>how it works</strong>, and <strong>benefits</strong> for each case.</p>

<hr />

<h2 id="1-payment-gateway-integration">1. Payment Gateway Integration</h2>

<h3 id="use-case">Use Case</h3>

<p>Payment Processing System</p>

<h3 id="problem">Problem</h3>

<p>An application must support multiple payment providers such as PayPal, Stripe, and Bank Transfer. Hardcoding provider-specific logic makes the system difficult to extend and maintain.</p>

<h3 id="solution">Solution</h3>

<p>Use a factory to create the appropriate payment processor based on configuration or user choice.</p>

<h3 id="key-components">Key Components</h3>

<ul>
  <li>PaymentProcessor (interface)</li>
  <li>PayPalProcessor, StripeProcessor, BankTransferProcessor</li>
  <li>PaymentProcessorFactory</li>
</ul>

<h3 id="how-it-works">How It Works</h3>

<p>The client requests a payment processor from the factory. The factory decides which concrete implementation to return.</p>

<h3 id="benefits">Benefits</h3>

<ul>
  <li>Easy to add new payment gateways</li>
  <li>Clean separation of concerns</li>
  <li>Open/Closed Principle compliance</li>
</ul>

<hr />

<h2 id="2-notification-system">2. Notification System</h2>

<h3 id="use-case-1">Use Case</h3>

<p>Multi-channel Notification Service</p>

<h3 id="problem-1">Problem</h3>

<p>Sending notifications via Email, SMS, or Push requires different implementations and APIs.</p>

<h3 id="solution-1">Solution</h3>

<p>Use a notification factory to return the appropriate notification sender.</p>

<h3 id="key-components-1">Key Components</h3>

<ul>
  <li>INotification</li>
  <li>EmailNotification, SmsNotification, PushNotification</li>
  <li>NotificationFactory</li>
</ul>

<h3 id="how-it-works-1">How It Works</h3>

<p>The factory reads notification type and returns the correct sender implementation.</p>

<h3 id="benefits-1">Benefits</h3>

<ul>
  <li>Centralized creation logic</li>
  <li>Easy to extend notification channels</li>
  <li>Cleaner business logic</li>
</ul>

<hr />

<h2 id="3-logging-framework">3. Logging Framework</h2>

<h3 id="use-case-2">Use Case</h3>

<p>Application Logging</p>

<h3 id="problem-2">Problem</h3>

<p>Different environments require different logging mechanisms such as File, Database, or Cloud logging.</p>

<h3 id="solution-2">Solution</h3>

<p>Use a logger factory to create logger instances dynamically.</p>

<h3 id="key-components-2">Key Components</h3>

<ul>
  <li>ILogger</li>
  <li>FileLogger, DatabaseLogger, CloudLogger</li>
  <li>LoggerFactory</li>
</ul>

<h3 id="how-it-works-2">How It Works</h3>

<p>Based on environment or configuration, the factory returns the required logger.</p>

<h3 id="benefits-2">Benefits</h3>

<ul>
  <li>Environment-specific logging</li>
  <li>Improved configurability</li>
  <li>Reduced code duplication</li>
</ul>

<hr />

<h2 id="4-database-connection-management">4. Database Connection Management</h2>

<h3 id="use-case-3">Use Case</h3>

<p>Multiple Database Support</p>

<h3 id="problem-3">Problem</h3>

<p>Applications may need to support SQL Server, MySQL, or PostgreSQL without rewriting data access logic.</p>

<h3 id="solution-3">Solution</h3>

<p>Use a database connection factory.</p>

<h3 id="key-components-3">Key Components</h3>

<ul>
  <li>IDbConnection</li>
  <li>SqlConnection, MySqlConnection, PostgreSqlConnection</li>
  <li>DbConnectionFactory</li>
</ul>

<h3 id="how-it-works-3">How It Works</h3>

<p>The factory reads database type from configuration and returns the appropriate connection.</p>

<h3 id="benefits-3">Benefits</h3>

<ul>
  <li>Database agnostic code</li>
  <li>Easier migration</li>
  <li>Better testability</li>
</ul>

<hr />

<h2 id="5-document-generation-system">5. Document Generation System</h2>

<h3 id="use-case-4">Use Case</h3>

<p>Report and Document Creation</p>

<h3 id="problem-4">Problem</h3>

<p>Generating documents in PDF, Excel, or Word formats requires different libraries and logic.</p>

<h3 id="solution-4">Solution</h3>

<p>Use a document factory to create document generators.</p>

<h3 id="key-components-4">Key Components</h3>

<ul>
  <li>IDocumentGenerator</li>
  <li>PdfGenerator, ExcelGenerator, WordGenerator</li>
  <li>DocumentFactory</li>
</ul>

<h3 id="how-it-works-4">How It Works</h3>

<p>The factory returns a document generator based on requested format.</p>

<h3 id="benefits-4">Benefits</h3>

<ul>
  <li>Consistent document creation</li>
  <li>Easy format extension</li>
  <li>Reduced conditional logic</li>
</ul>

<hr />

<h2 id="6-ui-component-creation">6. UI Component Creation</h2>

<h3 id="use-case-5">Use Case</h3>

<p>Cross-platform UI Rendering</p>

<h3 id="problem-5">Problem</h3>

<p>UI components vary across platforms (Web, Desktop, Mobile).</p>

<h3 id="solution-5">Solution</h3>

<p>Use a factory to create platform-specific UI components.</p>

<h3 id="key-components-5">Key Components</h3>

<ul>
  <li>IButton, ITextBox</li>
  <li>WebButton, DesktopButton, MobileButton</li>
  <li>UIFactory</li>
</ul>

<h3 id="how-it-works-5">How It Works</h3>

<p>The factory creates UI components according to platform context.</p>

<h3 id="benefits-5">Benefits</h3>

<ul>
  <li>Platform independence</li>
  <li>Improved maintainability</li>
  <li>Clean UI abstraction</li>
</ul>

<hr />

<h2 id="7-file-parsing-system">7. File Parsing System</h2>

<h3 id="use-case-6">Use Case</h3>

<p>Importing Different File Formats</p>

<h3 id="problem-6">Problem</h3>

<p>Applications need to process CSV, JSON, and XML files differently.</p>

<h3 id="solution-6">Solution</h3>

<p>Use a parser factory.</p>

<h3 id="key-components-6">Key Components</h3>

<ul>
  <li>IFileParser</li>
  <li>CsvParser, JsonParser, XmlParser</li>
  <li>FileParserFactory</li>
</ul>

<h3 id="how-it-works-6">How It Works</h3>

<p>The factory inspects file type and returns the correct parser.</p>

<h3 id="benefits-6">Benefits</h3>

<ul>
  <li>Simplified file processing</li>
  <li>Easy format addition</li>
  <li>Improved readability</li>
</ul>

<hr />

<h2 id="8-authentication-mechanism">8. Authentication Mechanism</h2>

<h3 id="use-case-7">Use Case</h3>

<p>Multiple Authentication Providers</p>

<h3 id="problem-7">Problem</h3>

<p>Supporting OAuth, JWT, LDAP, or API Key authentication increases complexity.</p>

<h3 id="solution-7">Solution</h3>

<p>Use an authentication factory.</p>

<h3 id="key-components-7">Key Components</h3>

<ul>
  <li>IAuthenticator</li>
  <li>JwtAuthenticator, OAuthAuthenticator, LdapAuthenticator</li>
  <li>AuthenticatorFactory</li>
</ul>

<h3 id="how-it-works-7">How It Works</h3>

<p>The factory selects an authentication strategy based on request or configuration.</p>

<h3 id="benefits-7">Benefits</h3>

<ul>
  <li>Flexible authentication strategies</li>
  <li>Better security management</li>
  <li>Cleaner controller code</li>
</ul>

<hr />

<h2 id="9-cloud-resource-provisioning">9. Cloud Resource Provisioning</h2>

<h3 id="use-case-8">Use Case</h3>

<p>Multi-cloud Application</p>

<h3 id="problem-8">Problem</h3>

<p>Different cloud providers require different APIs for resource creation.</p>

<h3 id="solution-8">Solution</h3>

<p>Use a cloud service factory.</p>

<h3 id="key-components-8">Key Components</h3>

<ul>
  <li>ICloudService</li>
  <li>AwsService, AzureService, GcpService</li>
  <li>CloudServiceFactory</li>
</ul>

<h3 id="how-it-works-8">How It Works</h3>

<p>The factory returns the correct cloud service implementation.</p>

<h3 id="benefits-8">Benefits</h3>

<ul>
  <li>Cloud provider abstraction</li>
  <li>Vendor lock-in reduction</li>
  <li>Scalable architecture</li>
</ul>

<hr />

<h2 id="10-game-character-creation">10. Game Character Creation</h2>

<h3 id="use-case-9">Use Case</h3>

<p>Game Development</p>

<h3 id="problem-9">Problem</h3>

<p>Games require different types of characters such as Warrior, Mage, and Archer with unique behaviors.</p>

<h3 id="solution-9">Solution</h3>

<p>Use a character factory.</p>

<h3 id="key-components-9">Key Components</h3>

<ul>
  <li>IGameCharacter</li>
  <li>Warrior, Mage, Archer</li>
  <li>CharacterFactory</li>
</ul>

<h3 id="how-it-works-9">How It Works</h3>

<p>The factory creates character objects based on player choice or game logic.</p>

<h3 id="benefits-9">Benefits</h3>

<ul>
  <li>Simplified object creation</li>
  <li>Easy character expansion</li>
  <li>Cleaner game logic</li>
</ul>

<hr />

<h2 id="conclusion">Conclusion</h2>

<p>The Factory Design Pattern is a <strong>practical and powerful solution</strong> for managing object creation in real-world applications. It promotes:</p>

<ul>
  <li>Loose coupling</li>
  <li>Scalability</li>
  <li>Maintainability</li>
  <li>Clean architecture</li>
</ul>

<p>From enterprise systems to game development, factories help keep code <strong>flexible and future-proof</strong>.</p>]]></content><author><name>Mahedee Hasan</name></author><category term="OOP" /><category term="Software Architecture" /><category term="Design Pattern" /><category term="oop" /><category term="designpattern" /><category term="factory" /><category term="creational" /><category term="realworld" /><category term="examples" /><category term="implementation" /><category term="architecture" /><summary type="html"><![CDATA[Explore 10 practical real-world applications of the Factory Design Pattern, from payment gateways and notification systems to cloud services and game development. Learn implementation strategies and benefits.]]></summary></entry><entry><title type="html">Factory Design Pattern using C# with a real world banking example</title><link href="https://mahedee.net/factory-design-pattern-using-csharp/" rel="alternate" type="text/html" title="Factory Design Pattern using C# with a real world banking example" /><published>2026-01-01T00:00:00-05:00</published><updated>2026-01-01T00:00:00-05:00</updated><id>https://mahedee.net/factory-design-pattern-using-csharp</id><content type="html" xml:base="https://mahedee.net/factory-design-pattern-using-csharp/"><![CDATA[<p>The Factory Design Pattern is one of the most fundamental and widely-used creational patterns in software development. It mirrors the concept of real-world factories by centralizing object creation logic and abstracting the instantiation process from client code.</p>

<h2 id="what-is-the-factory-design-pattern">What is the Factory Design Pattern?</h2>

<p>The Factory Pattern is a <strong>creational design pattern</strong> that provides an interface for creating objects without specifying their exact classes. Instead of directly instantiating objects using the <code class="language-plaintext highlighter-rouge">new</code> keyword, clients delegate this responsibility to a factory class.</p>

<p>The pattern involves three main actors:</p>
<ul>
  <li><strong>Client</strong>: The object that needs another object for specific purposes</li>
  <li><strong>Factory</strong>: The class responsible for creating instances</li>
  <li><strong>Product</strong>: The objects being created (typically implementing a common interface)</li>
</ul>

<p><img src="/assets/images/posts/2026/factory_pattern.png" alt="Factory Pattern Process" /></p>

<h2 id="real-world-application-banking-system">Real-World Application: Banking System</h2>

<p>Let’s explore the Factory Pattern through a banking application that manages different types of accounts. In this system:</p>

<ul>
  <li><strong>Multiple account types</strong> exist (Savings Account, Checking Account)</li>
  <li><strong>All accounts</strong> implement a common interface (<code class="language-plaintext highlighter-rouge">IAccount</code>)</li>
  <li><strong>Clients</strong> need account instances without knowing implementation details</li>
  <li><strong>New account types</strong> can be added without modifying existing client code</li>
</ul>

<p>When a client needs to know the interest rate for a Savings Account, it simply requests the factory to create an instance. The factory handles the instantiation logic, returns the appropriate account type, and the client works with the object through the common interface—completely unaware of the concrete implementation.</p>

<p>This approach provides several key advantages:</p>
<ul>
  <li><strong>Centralized object creation</strong>: All instantiation logic is located in one place</li>
  <li><strong>Loose coupling</strong>: Clients depend only on interfaces, not concrete classes</li>
  <li><strong>Easy extensibility</strong>: New account types can be added without modifying client code</li>
</ul>

<h2 id="class-diagram">Class Diagram</h2>

<p>The following class diagram illustrates the structure of our Factory Pattern implementation:</p>

<p><img src="/assets/images/posts/2026/factory-pattern-class-diagram.png" alt="" /></p>

<p><strong>Key Relationships:</strong></p>
<ul>
  <li><strong>Inheritance</strong>: All concrete account classes implement the <code class="language-plaintext highlighter-rouge">IAccount</code> interface</li>
  <li><strong>Dependency</strong>: <code class="language-plaintext highlighter-rouge">AccountFactory</code> depends on <code class="language-plaintext highlighter-rouge">AccountType</code> enum and creates <code class="language-plaintext highlighter-rouge">IAccount</code> instances</li>
  <li><strong>Usage</strong>: <code class="language-plaintext highlighter-rouge">BankingApplication</code> (client) uses the factory to obtain accounts and works with them through the interface</li>
  <li><strong>Creation</strong>: The factory creates concrete instances but returns them as interface references</li>
</ul>

<p>This diagram shows how the Factory Pattern decouples the client code from concrete implementations, allowing for easy extension and maintenance.</p>

<h2 id="key-benefits">Key Benefits</h2>

<ul>
  <li><strong>Most widely adopted pattern</strong>: Extensively used across enterprise applications</li>
  <li><strong>Loose coupling</strong>: Eliminates tight binding between client code and specific implementations</li>
  <li><strong>Interface-based design</strong>: Client code works exclusively with interfaces</li>
  <li><strong>Flexible object creation</strong>: Supports multiple instances and object variations</li>
  <li><strong>Extensibility</strong>: Enables subclassing and polymorphic behavior</li>
  <li><strong>Minimal coupling</strong>: Connects class hierarchies with reduced dependencies</li>
  <li><strong>Future-proof</strong>: Product implementations can evolve without affecting clients</li>
</ul>

<h2 id="implementation-guide">Implementation Guide</h2>

<p>Let’s build our banking system step by step to demonstrate the Factory Pattern in action.</p>

<h3 id="step-1-define-the-product-interface">Step 1: Define the Product Interface</h3>

<p>First, we create the <code class="language-plaintext highlighter-rouge">IAccount</code> interface that defines the contract for all account types:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Common interface for all account types</span>
    <span class="c1">/// Defines the contract that all account implementations must follow</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IAccount</span>
    <span class="p">{</span>
        <span class="kt">string</span> <span class="nf">Withdraw</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">);</span>
        <span class="kt">string</span> <span class="nf">Deposit</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">);</span>
        <span class="kt">double</span> <span class="nf">GetInterestRate</span><span class="p">();</span>
        <span class="kt">string</span> <span class="nf">GetAccountType</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-2-implement-concrete-products">Step 2: Implement Concrete Products</h3>

<p>Now we’ll create concrete implementations of different account types.</p>

<h4 id="savings-account-implementation">Savings Account Implementation</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Concrete implementation of a Savings Account</span>
    <span class="c1">/// Provides higher interest rates but may have withdrawal limitations</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">SavingsAccount</span> <span class="p">:</span> <span class="n">IAccount</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="kt">decimal</span> <span class="n">balance</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Withdraw</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">amount</span> <span class="p">&lt;=</span> <span class="n">balance</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">balance</span> <span class="p">-=</span> <span class="n">amount</span><span class="p">;</span>
                <span class="k">return</span> <span class="s">$"Withdrew $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s">. New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="s">"Insufficient funds for withdrawal."</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Deposit</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">balance</span> <span class="p">+=</span> <span class="n">amount</span><span class="p">;</span>
            <span class="k">return</span> <span class="s">$"Deposited $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s">. New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">double</span> <span class="nf">GetInterestRate</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="m">2.5</span><span class="p">;</span> <span class="c1">// 2.5% interest rate for savings</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">GetAccountType</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">"Savings Account"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="checking-account-implementation">Checking Account Implementation</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Concrete implementation of a Checking Account</span>
    <span class="c1">/// Provides easier access to funds but typically lower interest rates</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">CheckingAccount</span> <span class="p">:</span> <span class="n">IAccount</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="kt">decimal</span> <span class="n">balance</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Withdraw</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Checking accounts might allow overdrafts</span>
            <span class="n">balance</span> <span class="p">-=</span> <span class="n">amount</span><span class="p">;</span>
            <span class="k">return</span> <span class="s">$"Withdrew $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s">. New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Deposit</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">balance</span> <span class="p">+=</span> <span class="n">amount</span><span class="p">;</span>
            <span class="k">return</span> <span class="s">$"Deposited $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s">. New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">double</span> <span class="nf">GetInterestRate</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="m">0.1</span><span class="p">;</span> <span class="c1">// 0.1% interest rate for checking</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">GetAccountType</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">"Checking Account"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-3-create-account-type-enumeration">Step 3: Create Account Type Enumeration</h3>

<p>Define an enumeration to specify which type of account to create:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Enumeration defining the types of accounts available</span>
    <span class="c1">/// Used by the factory to determine which concrete class to instantiate</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">enum</span> <span class="n">AccountType</span>
    <span class="p">{</span>
        <span class="n">Savings</span><span class="p">,</span>
        <span class="n">Checking</span><span class="p">,</span>
        <span class="n">BusinessChecking</span> <span class="c1">// Example of easy extensibility</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-4-implement-the-factory-class">Step 4: Implement the Factory Class</h3>

<p>The factory class contains the core logic for object creation:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Factory class responsible for creating account instances</span>
    <span class="c1">/// Centralizes object creation logic and provides a single point of control</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">AccountFactory</span>
    <span class="p">{</span>
        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Creates an account instance based on the specified type</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;param name="accountType"&gt;The type of account to create&lt;/param&gt;</span>
        <span class="c1">/// &lt;returns&gt;An IAccount instance of the specified type&lt;/returns&gt;</span>
        <span class="c1">/// &lt;exception cref="ArgumentException"&gt;Thrown when an invalid account type is provided&lt;/exception&gt;</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">IAccount</span> <span class="nf">CreateAccount</span><span class="p">(</span><span class="n">AccountType</span> <span class="n">accountType</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">accountType</span> <span class="k">switch</span>
            <span class="p">{</span>
                <span class="n">AccountType</span><span class="p">.</span><span class="n">Savings</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">SavingsAccount</span><span class="p">(),</span>
                <span class="n">AccountType</span><span class="p">.</span><span class="n">Checking</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CheckingAccount</span><span class="p">(),</span>
                <span class="n">AccountType</span><span class="p">.</span><span class="n">BusinessChecking</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">BusinessCheckingAccount</span><span class="p">(),</span> <span class="c1">// Future extension</span>
                <span class="n">_</span> <span class="p">=&gt;</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">$"Unknown account type: </span><span class="p">{</span><span class="n">accountType</span><span class="p">}</span><span class="s">"</span><span class="p">)</span>
            <span class="p">};</span>
        <span class="p">}</span>
        
        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Overloaded method that creates an account based on string input</span>
        <span class="c1">/// Useful for scenarios where account type comes from user input or configuration</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;param name="accountTypeName"&gt;String representation of the account type&lt;/param&gt;</span>
        <span class="c1">/// &lt;returns&gt;An IAccount instance of the specified type&lt;/returns&gt;</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">IAccount</span> <span class="nf">CreateAccount</span><span class="p">(</span><span class="kt">string</span> <span class="n">accountTypeName</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">Enum</span><span class="p">.</span><span class="n">TryParse</span><span class="p">&lt;</span><span class="n">AccountType</span><span class="p">&gt;(</span><span class="n">accountTypeName</span><span class="p">,</span> <span class="k">true</span><span class="p">,</span> <span class="k">out</span> <span class="n">AccountType</span> <span class="n">accountType</span><span class="p">))</span>
            <span class="p">{</span>
                <span class="k">return</span> <span class="nf">CreateAccount</span><span class="p">(</span><span class="n">accountType</span><span class="p">);</span>
            <span class="p">}</span>
            
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">$"Invalid account type name: </span><span class="p">{</span><span class="n">accountTypeName</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="step-5-client-implementation-and-usage">Step 5: Client Implementation and Usage</h3>

<p>Finally, let’s see how the client code uses the factory:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Client application demonstrating the Factory Pattern usage</span>
    <span class="c1">/// Notice how the client code is completely decoupled from concrete implementations</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">BankingApplication</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"=== Banking System - Factory Pattern Demo ===\n"</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Create different types of accounts using the factory</span>
                <span class="n">IAccount</span> <span class="n">savingsAccount</span> <span class="p">=</span> <span class="n">AccountFactory</span><span class="p">.</span><span class="nf">CreateAccount</span><span class="p">(</span><span class="n">AccountType</span><span class="p">.</span><span class="n">Savings</span><span class="p">);</span>
                <span class="n">IAccount</span> <span class="n">checkingAccount</span> <span class="p">=</span> <span class="n">AccountFactory</span><span class="p">.</span><span class="nf">CreateAccount</span><span class="p">(</span><span class="n">AccountType</span><span class="p">.</span><span class="n">Checking</span><span class="p">);</span>
                
                <span class="c1">// Alternative: Create account using string input (useful for user interfaces)</span>
                <span class="n">IAccount</span> <span class="n">userAccount</span> <span class="p">=</span> <span class="n">AccountFactory</span><span class="p">.</span><span class="nf">CreateAccount</span><span class="p">(</span><span class="s">"Savings"</span><span class="p">);</span>
                
                <span class="c1">// Use the accounts without knowing their concrete types</span>
                <span class="nf">DisplayAccountInfo</span><span class="p">(</span><span class="n">savingsAccount</span><span class="p">);</span>
                <span class="nf">DisplayAccountInfo</span><span class="p">(</span><span class="n">checkingAccount</span><span class="p">);</span>
                
                <span class="c1">// Perform operations</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"\n=== Account Operations ==="</span><span class="p">);</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">savingsAccount</span><span class="p">.</span><span class="nf">Deposit</span><span class="p">(</span><span class="m">1000</span><span class="p">));</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">savingsAccount</span><span class="p">.</span><span class="nf">Withdraw</span><span class="p">(</span><span class="m">250</span><span class="p">));</span>
                
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">checkingAccount</span><span class="p">.</span><span class="nf">Deposit</span><span class="p">(</span><span class="m">500</span><span class="p">));</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">checkingAccount</span><span class="p">.</span><span class="nf">Withdraw</span><span class="p">(</span><span class="m">600</span><span class="p">));</span> <span class="c1">// This might overdraft</span>
                
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">ArgumentException</span> <span class="n">ex</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Error: </span><span class="p">{</span><span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"\nPress any key to exit..."</span><span class="p">);</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">ReadKey</span><span class="p">();</span>
        <span class="p">}</span>
        
        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Helper method to display account information</span>
        <span class="c1">/// Demonstrates working with objects through their interface</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;param name="account"&gt;Account instance to display information for&lt;/param&gt;</span>
        <span class="k">private</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">DisplayAccountInfo</span><span class="p">(</span><span class="n">IAccount</span> <span class="n">account</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Account Type: </span><span class="p">{</span><span class="n">account</span><span class="p">.</span><span class="nf">GetAccountType</span><span class="p">()}</span><span class="s">"</span><span class="p">);</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Interest Rate: </span><span class="p">{</span><span class="n">account</span><span class="p">.</span><span class="nf">GetInterestRate</span><span class="p">()}</span><span class="s">%"</span><span class="p">);</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="extending-the-pattern-adding-new-account-types">Extending the Pattern: Adding New Account Types</h2>

<p>One of the Factory Pattern’s greatest strengths is its extensibility. Let’s add a new account type without modifying existing code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">FactoryPattern</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// New account type demonstrating pattern extensibility</span>
    <span class="c1">/// Added without modifying any existing client code</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">BusinessCheckingAccount</span> <span class="p">:</span> <span class="n">IAccount</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="kt">decimal</span> <span class="n">balance</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Withdraw</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Business accounts might have different fee structures</span>
            <span class="kt">decimal</span> <span class="n">fee</span> <span class="p">=</span> <span class="m">2.50m</span><span class="p">;</span> <span class="c1">// Transaction fee</span>
            <span class="kt">decimal</span> <span class="n">totalAmount</span> <span class="p">=</span> <span class="n">amount</span> <span class="p">+</span> <span class="n">fee</span><span class="p">;</span>
            
            <span class="k">if</span> <span class="p">(</span><span class="n">totalAmount</span> <span class="p">&lt;=</span> <span class="n">balance</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">balance</span> <span class="p">-=</span> <span class="n">totalAmount</span><span class="p">;</span>
                <span class="k">return</span> <span class="s">$"Withdrew $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s"> ($</span><span class="p">{</span><span class="n">fee</span><span class="p">}</span><span class="s"> fee). New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="s">"Insufficient funds for withdrawal including fees."</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">Deposit</span><span class="p">(</span><span class="kt">decimal</span> <span class="n">amount</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">balance</span> <span class="p">+=</span> <span class="n">amount</span><span class="p">;</span>
            <span class="k">return</span> <span class="s">$"Business deposit: $</span><span class="p">{</span><span class="n">amount</span><span class="p">}</span><span class="s">. New balance: $</span><span class="p">{</span><span class="n">balance</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">double</span> <span class="nf">GetInterestRate</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="m">0.05</span><span class="p">;</span> <span class="c1">// Lower rate but business-focused features</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="kt">string</span> <span class="nf">GetAccountType</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">"Business Checking Account"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To use this new account type, simply update the factory’s switch statement and the enumeration. No client code needs modification!</p>

<h2 id="best-practices-and-considerations">Best Practices and Considerations</h2>

<h3 id="when-to-use-the-factory-pattern">When to Use the Factory Pattern</h3>

<ul>
  <li><strong>Multiple related classes</strong> need to be created</li>
  <li><strong>Object creation logic</strong> is complex or likely to change</li>
  <li><strong>Client code</strong> should be decoupled from specific implementations</li>
  <li><strong>Runtime decisions</strong> determine which class to instantiate</li>
</ul>

<h3 id="design-considerations">Design Considerations</h3>

<ul>
  <li><strong>Keep factories focused</strong>: Each factory should handle related object types</li>
  <li><strong>Use dependency injection</strong>: Consider integrating with DI containers for better testability</li>
  <li><strong>Handle errors gracefully</strong>: Provide clear error messages for invalid inputs</li>
  <li><strong>Document factory methods</strong>: Make it clear what each factory method produces</li>
</ul>

<h3 id="advanced-variations">Advanced Variations</h3>

<ul>
  <li><strong>Abstract Factory</strong>: For creating families of related objects</li>
  <li><strong>Factory Method</strong>: Using inheritance to delegate object creation to subclasses</li>
  <li><strong>Registration-based Factory</strong>: Using reflection or registration mechanisms for dynamic type discovery</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The Factory Design Pattern provides a robust solution for managing object creation while maintaining loose coupling and high extensibility. Through our banking system example, we’ve seen how this pattern:</p>

<ul>
  <li><strong>Centralizes</strong> object creation logic</li>
  <li><strong>Enables</strong> easy addition of new types</li>
  <li><strong>Maintains</strong> clean separation of concerns</li>
  <li><strong>Simplifies</strong> client code</li>
</ul>

<p>By implementing the Factory Pattern, your applications become more maintainable, testable, and ready for future enhancements. The pattern’s widespread adoption in enterprise applications speaks to its effectiveness in solving real-world object creation challenges.</p>

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/master/factory-pattern">Source code available on GitHub</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term="OOP" /><category term="C#" /><category term="Design Pattern" /><category term="oop" /><category term="csharp" /><category term="designpattern" /><category term="factory" /><category term="creational" /><category term="reviewed_v02" /><summary type="html"><![CDATA[Learn how to implement the Factory Design Pattern in C# through a practical banking application example. Understand object creation delegation, loose coupling, and extensible design principles.]]></summary></entry><entry><title type="html">Implementing Metrics and Dashboards for .NET Core APIs with Prometheus and Grafana</title><link href="https://mahedee.net/implementing-metrics-and-dashboards-dotnet-core-api-prometheus-grafana/" rel="alternate" type="text/html" title="Implementing Metrics and Dashboards for .NET Core APIs with Prometheus and Grafana" /><published>2025-12-28T00:00:00-05:00</published><updated>2025-12-28T00:00:00-05:00</updated><id>https://mahedee.net/implementing-metrics-and-dashboards-dotnet-core-api-prometheus-grafana</id><content type="html" xml:base="https://mahedee.net/implementing-metrics-and-dashboards-dotnet-core-api-prometheus-grafana/"><![CDATA[<!-- You can add a article banner here -->

<p><strong>Introduction</strong></p>

<p>In today’s fast-paced software development environment, auditing, security, monitoring the performance and health of your applications is crucial. For .NET Core APIs, integrating Prometheus for metrics collection and Grafana for visualization provides a powerful solution for observability. This article will guide you through the process of implementing metrics and dashboards for your .NET Core APIs using Prometheus and Grafana.</p>

<p><strong>What is Prometheus?</strong>
Prometheus is an open-source systems monitoring and alerting toolkit originally built at SoundCloud. It collects and stores metrics as time series data, allowing you to monitor your applications and infrastructure effectively. Prometheus support for different types of matrics, including counters, gauges, histograms, and summaries.</p>

<p><strong>What is Grafana?</strong>
Grafana is an open-source platform for monitoring and observability that allows you to visualize data from various sources, including Prometheus. It provides a rich set of features for creating interactive and customizable dashboards. It supports visualize data through a pluggable architecture. It supports various data sources, including Prometheus, InfluxDB, Elasticsearch, and many others.</p>

<p><strong>Features Offered by Prometheus and Grafana</strong></p>
<ul>
  <li><strong>Metrics Collection</strong>: Prometheus collects metrics from your .NET Core APIs, including request rates, error rates, and response times.</li>
  <li><strong>Data Storage</strong>: Prometheus stores the collected metrics in a time-series database, allowing for efficient querying and analysis.</li>
  <li><strong>Visualization</strong>: Grafana provides a user-friendly interface to create dashboards and visualize the metrics collected by Prometheus.</li>
  <li><strong>Alerting</strong>: Prometheus can be configured to send alerts based on specific conditions, helping you proactively monitor your applications. For example, you can set up alerts for high error rates or slow response times.</li>
  <li><strong>Custom Dashboards</strong>: Grafana allows you to create custom dashboards tailored to your monitoring needs, enabling you to focus on the metrics that matter most to your applications.</li>
</ul>

<p><strong>Pros and Cons of Using Prometheus and Grafana</strong>
<strong>Pros:</strong></p>
<ul>
  <li>Open-source and free to use.</li>
  <li>Highly customizable and extensible.</li>
  <li>Strong community support and extensive documentation.</li>
  <li>Seamless integration with .NET Core APIs.</li>
  <li>Real-time monitoring and alerting capabilities.</li>
  <li>Broad compatibility with various data sources.</li>
</ul>

<p><strong>Cons:</strong></p>
<ul>
  <li>Requires initial setup and configuration.</li>
  <li>May have a learning curve for beginners.</li>
  <li>Resource-intensive for large-scale deployments.</li>
</ul>

<p><strong>Step-by-Step Guide to Implementing Metrics and Dashboards</strong></p>

<p><strong>Step 1: Create a .NET Core API Project</strong>
Start by creating a new .NET Core API project using the .NET CLI or Visual Studio.</p>

<ul>
  <li>Create a solution and a new Web API project:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new webapi <span class="nt">-n</span> EcommerceApi
<span class="nb">cd </span>EcommerceApi
dotnet new sln <span class="nt">-n</span> EcommerceSolution
dotnet sln add .<span class="se">\E</span>commerceApi.csproj
</code></pre></div></div>

<p><strong>Step 2: Install the Required NuGet Packages</strong></p>

<p>Install the following NuGet packages for metrics collection and Prometheus integration:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package Microsoft.EntityFrameworkCore.InMemory <span class="nt">--version</span> 9.0.0
dotnet add package Swashbuckle.AspNetCore <span class="nt">--version</span> 6.8.1
dotnet add package App.Metrics <span class="nt">--version</span> 4.3.0
dotnet add package App.Metrics.AspNetCore <span class="nt">--version</span> 4.3.0
dotnet add package App.Metrics.AspNetCore.Endpoints <span class="nt">--version</span> 4.3.0
dotnet add package App.Metrics.Formatters.Prometheus <span class="nt">--version</span> 4.3.0
</code></pre></div></div>

<p><strong>Package Overview:</strong></p>
<ul>
  <li><strong>Microsoft.EntityFrameworkCore.InMemory</strong>: Provides in-memory database capabilities for development and testing.</li>
  <li><strong>Swashbuckle.AspNetCore</strong>: Enables Swagger/OpenAPI documentation for your API endpoints.</li>
  <li><strong>App.Metrics</strong>: Core metrics library that provides various metric types (counters, gauges, histograms).</li>
  <li><strong>App.Metrics.AspNetCore</strong>: ASP.NET Core integration for automatic HTTP metrics collection.</li>
  <li><strong>App.Metrics.AspNetCore.Endpoints</strong>: Provides endpoints to expose metrics data via HTTP.</li>
  <li><strong>App.Metrics.Formatters.Prometheus</strong>: Formats metrics data in Prometheus-compatible format for scraping.</li>
</ul>

<p><strong>Step 3: Create Product Entity in Entities Folder</strong>
Create a new folder named <code class="language-plaintext highlighter-rouge">Entities</code> and add a <code class="language-plaintext highlighter-rouge">Product.cs</code> file to define the Product entity.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">namespace</span> <span class="nn">EcommerceApi.Entities</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Product</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">Description</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
        <span class="k">public</span> <span class="kt">decimal</span> <span class="n">Price</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">int</span> <span class="n">StockQuantity</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p><strong>Step 4: Set Up the Database Context in Data Folder</strong>
Create a new folder named <code class="language-plaintext highlighter-rouge">Data</code> and add an <code class="language-plaintext highlighter-rouge">EcommerceDbContext.cs</code> file to set up the database context.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">EcommerceApi.Entities</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EcommerceApi.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">EcommerceDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nf">EcommerceDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">EcommerceDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
        <span class="p">{</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="n">Products</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">ModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">base</span><span class="p">.</span><span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">modelBuilder</span><span class="p">);</span>

            <span class="c1">// Seed some initial data for testing</span>
            <span class="n">modelBuilder</span><span class="p">.</span><span class="n">Entity</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;().</span><span class="nf">HasData</span><span class="p">(</span>
                <span class="k">new</span> <span class="n">Product</span>
                <span class="p">{</span>
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">1</span><span class="p">,</span>
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Laptop"</span><span class="p">,</span>
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"High-performance laptop for gaming and work"</span><span class="p">,</span>
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">999.99m</span><span class="p">,</span>
                    <span class="n">StockQuantity</span> <span class="p">=</span> <span class="m">50</span>
                <span class="p">},</span>
                <span class="k">new</span> <span class="n">Product</span>
                <span class="p">{</span>
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">2</span><span class="p">,</span>
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Smartphone"</span><span class="p">,</span>
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"Latest smartphone with advanced features"</span><span class="p">,</span>
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">699.99m</span><span class="p">,</span>
                    <span class="n">StockQuantity</span> <span class="p">=</span> <span class="m">100</span>
                <span class="p">},</span>
                <span class="k">new</span> <span class="n">Product</span>
                <span class="p">{</span>
                    <span class="n">Id</span> <span class="p">=</span> <span class="m">3</span><span class="p">,</span>
                    <span class="n">Name</span> <span class="p">=</span> <span class="s">"Headphones"</span><span class="p">,</span>
                    <span class="n">Description</span> <span class="p">=</span> <span class="s">"Wireless noise-canceling headphones"</span><span class="p">,</span>
                    <span class="n">Price</span> <span class="p">=</span> <span class="m">199.99m</span><span class="p">,</span>
                    <span class="n">StockQuantity</span> <span class="p">=</span> <span class="m">75</span>
                <span class="p">}</span>
            <span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Step 5: Create Product Repository in Repositories Folder</strong></p>

<p>Create a new folder named <code class="language-plaintext highlighter-rouge">Repositories</code> and add a <code class="language-plaintext highlighter-rouge">IProductRepository.cs</code> file to handle data access for products.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">EcommerceApi.Entities</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EcommerceApi.Repositories</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IProductRepository</span>
    <span class="p">{</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">GetAllAsync</span><span class="p">();</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">?&gt;</span> <span class="nf">GetByIdAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">);</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="nf">CreateAsync</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">);</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="nf">UpdateAsync</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">);</span>
        <span class="n">Task</span><span class="p">&lt;</span><span class="kt">bool</span><span class="p">&gt;</span> <span class="nf">DeleteAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Create a <code class="language-plaintext highlighter-rouge">ProductRepository.cs</code> file to implement the repository interface.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">EcommerceApi.Entities</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EcommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EcommerceApi.Repositories</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductRepository</span> <span class="p">:</span> <span class="n">IProductRepository</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">EcommerceDbContext</span> <span class="n">_context</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">ProductRepository</span><span class="p">(</span><span class="n">EcommerceDbContext</span> <span class="n">context</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">GetAllAsync</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">ToListAsync</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">?&gt;</span> <span class="nf">GetByIdAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">FindAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="nf">CreateAsync</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
            <span class="k">return</span> <span class="n">product</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="nf">UpdateAsync</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
            <span class="k">return</span> <span class="n">product</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">bool</span><span class="p">&gt;</span> <span class="nf">DeleteAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">GetByIdAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="k">return</span> <span class="k">false</span><span class="p">;</span>

            <span class="n">_context</span><span class="p">.</span><span class="n">Products</span><span class="p">.</span><span class="nf">Remove</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">SaveChangesAsync</span><span class="p">();</span>
            <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Step 6: Register Metrics in Metrics Folder</strong>
Create a new folder named <code class="language-plaintext highlighter-rouge">Metrics</code> and add a <code class="language-plaintext highlighter-rouge">MetricsRegistry.cs</code> file to define custom metrics.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">using</span> <span class="nn">App.Metrics</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.Counter</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.Histogram</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.Timer</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.Gauge</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EcommerceApi.Metrics</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Centralized metrics registry for tracking various API performance and business metrics</span>
    <span class="c1">/// Used for monitoring API usage, performance, errors, and business KPIs in Grafana dashboards</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">MetricsRegistry</span>
    <span class="p">{</span>
        <span class="err">#</span><span class="n">region</span> <span class="n">Counter</span> <span class="n">Metrics</span> <span class="p">-</span> <span class="n">Track</span> <span class="n">API</span> <span class="n">Call</span> <span class="n">Frequency</span>
        
        <span class="c1">// Product Read Operations</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">GetByProductIdCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"get_product_by_id_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Calls</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"operation"</span><span class="p">,</span> <span class="s">"read"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">GetAllProductsCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"get_all_products_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span> 
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Calls</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"operation"</span><span class="p">,</span> <span class="s">"read"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="c1">// Product Write Operations</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">CreateProductCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"create_product_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Calls</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"operation"</span><span class="p">,</span> <span class="s">"create"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">UpdateProductCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"update_product_total"</span><span class="p">,</span> 
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Calls</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"operation"</span><span class="p">,</span> <span class="s">"update"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">DeleteProductCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"delete_product_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Calls</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"operation"</span><span class="p">,</span> <span class="s">"delete"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="c1">// Error Counters</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">ProductNotFoundCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"product_not_found_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Errors</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"error_type"</span><span class="p">,</span> <span class="s">"not_found"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">ValidationErrorCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"validation_error_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span> 
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Errors</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"error_type"</span><span class="p">,</span> <span class="s">"validation"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">ServerErrorCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"server_error_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Errors</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"error_type"</span><span class="p">,</span> <span class="s">"server_error"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>

        <span class="err">#</span><span class="n">region</span> <span class="n">Timer</span> <span class="n">Metrics</span> <span class="p">-</span> <span class="n">Track</span> <span class="n">Response</span> <span class="n">Time</span> <span class="n">Performance</span>

        <span class="c1">// API Response Time Timers</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">TimerOptions</span> <span class="n">GetProductByIdTimer</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">TimerOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"get_product_by_id_duration"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Requests</span><span class="p">,</span>
            <span class="n">DurationUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Milliseconds</span><span class="p">,</span>
            <span class="n">RateUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Seconds</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">TimerOptions</span> <span class="n">GetAllProductsTimer</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">TimerOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"get_all_products_duration"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span> 
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Requests</span><span class="p">,</span>
            <span class="n">DurationUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Milliseconds</span><span class="p">,</span>
            <span class="n">RateUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Seconds</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">TimerOptions</span> <span class="n">CreateProductTimer</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">TimerOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"create_product_duration"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Requests</span><span class="p">,</span>
            <span class="n">DurationUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Milliseconds</span><span class="p">,</span>
            <span class="n">RateUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Seconds</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">TimerOptions</span> <span class="n">UpdateProductTimer</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">TimerOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"update_product_duration"</span><span class="p">,</span> 
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Requests</span><span class="p">,</span>
            <span class="n">DurationUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Milliseconds</span><span class="p">,</span>
            <span class="n">RateUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Seconds</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">TimerOptions</span> <span class="n">DeleteProductTimer</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">TimerOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"delete_product_duration"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Requests</span><span class="p">,</span>
            <span class="n">DurationUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Milliseconds</span><span class="p">,</span>
            <span class="n">RateUnit</span> <span class="p">=</span> <span class="n">TimeUnit</span><span class="p">.</span><span class="n">Seconds</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>

        <span class="err">#</span><span class="n">region</span> <span class="n">Histogram</span> <span class="n">Metrics</span> <span class="p">-</span> <span class="n">Track</span> <span class="n">Distribution</span> <span class="n">of</span> <span class="n">Values</span>

        <span class="c1">// Request Size Distribution</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">HistogramOptions</span> <span class="n">RequestSizeHistogram</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">HistogramOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"request_size_bytes"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Bytes</span>
        <span class="p">};</span>

        <span class="c1">// Response Size Distribution  </span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">HistogramOptions</span> <span class="n">ResponseSizeHistogram</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">HistogramOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"response_size_bytes"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Bytes</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>

        <span class="err">#</span><span class="n">region</span> <span class="n">Gauge</span> <span class="n">Metrics</span> <span class="p">-</span> <span class="n">Track</span> <span class="n">Current</span> <span class="n">State</span> <span class="n">Values</span>

        <span class="c1">// Current System State</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">ActiveConnectionsGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"active_connections_current"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Connections</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">TotalProductsGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"total_products_current"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Items</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">MemoryUsageGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"memory_usage_bytes"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"ProductAPI"</span><span class="p">,</span> 
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Bytes</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>

        <span class="err">#</span><span class="n">region</span> <span class="n">Business</span> <span class="n">Metrics</span> <span class="p">-</span> <span class="n">Track</span> <span class="n">Business</span> <span class="n">KPIs</span>

        <span class="c1">// Business Intelligence Counters</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">ProductViewsCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"product_views_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"BusinessMetrics"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Events</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"metric_type"</span><span class="p">,</span> <span class="s">"engagement"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">CounterOptions</span> <span class="n">ProductCreatedTodayCounter</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">CounterOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"products_created_today_total"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"BusinessMetrics"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Items</span><span class="p">,</span>
            <span class="n">Tags</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricTags</span><span class="p">(</span><span class="s">"time_period"</span><span class="p">,</span> <span class="s">"daily"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="c1">// Average Product Price Gauge</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">AverageProductPriceGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"average_product_price"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"BusinessMetrics"</span><span class="p">,</span> 
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="nf">Custom</span><span class="p">(</span><span class="s">"currency"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>

        <span class="err">#</span><span class="n">region</span> <span class="n">Health</span> <span class="n">Check</span> <span class="n">Metrics</span>

        <span class="c1">// Database Health</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">DatabaseHealthGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"database_health_status"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"HealthCheck"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="nf">Custom</span><span class="p">(</span><span class="s">"status"</span><span class="p">)</span>
        <span class="p">};</span>

        <span class="c1">// API Health Score</span>
        <span class="k">public</span> <span class="k">static</span> <span class="n">GaugeOptions</span> <span class="n">ApiHealthScoreGauge</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">GaugeOptions</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"api_health_score"</span><span class="p">,</span>
            <span class="n">Context</span> <span class="p">=</span> <span class="s">"HealthCheck"</span><span class="p">,</span>
            <span class="n">MeasurementUnit</span> <span class="p">=</span> <span class="n">App</span><span class="p">.</span><span class="n">Metrics</span><span class="p">.</span><span class="n">Unit</span><span class="p">.</span><span class="n">Percent</span>
        <span class="p">};</span>

        <span class="err">#</span><span class="n">endregion</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Step 7: Create Product Controller in Controllers Folder</strong></p>
<ul>
  <li>Create a new folder named <code class="language-plaintext highlighter-rouge">Controllers</code> and add a <code class="language-plaintext highlighter-rouge">ProductsController.cs</code> file to handle API requests for products.</li>
  <li>Implement the controller with metrics tracking for each endpoint.</li>
  <li>In the below code, we have implemented metrics tracking for all CRUD operations on products.</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">EcommerceApi.Entities</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EcommerceApi.Repositories</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EcommerceApi.Metrics</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Mvc</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text.Json</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">EcommerceApi.Controllers</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
    <span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"api/[controller]"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ProductController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IProductRepository</span> <span class="n">_productRepository</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IMetrics</span> <span class="n">_metrics</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">ProductController</span><span class="p">(</span><span class="n">IProductRepository</span> <span class="n">productRepository</span><span class="p">,</span> <span class="n">IMetrics</span> <span class="n">metrics</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_productRepository</span> <span class="p">=</span> <span class="n">productRepository</span><span class="p">;</span>
            <span class="n">_metrics</span> <span class="p">=</span> <span class="n">metrics</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// GET: api/Product</span>
        <span class="p">[</span><span class="n">HttpGet</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">GetAllProducts</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="c1">// Start timing the operation</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Timer</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">GetAllProductsTimer</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Increment API call counter</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">GetAllProductsCounter</span><span class="p">);</span>

                <span class="kt">var</span> <span class="n">products</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetAllAsync</span><span class="p">();</span>
                
                <span class="c1">// Update business metrics</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">TotalProductsGauge</span><span class="p">,</span> <span class="n">products</span><span class="p">.</span><span class="nf">Count</span><span class="p">());</span>
                
                <span class="c1">// Calculate and update average product price</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">products</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">averagePrice</span> <span class="p">=</span> <span class="n">products</span><span class="p">.</span><span class="nf">Average</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">AverageProductPriceGauge</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">averagePrice</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Track response size</span>
                <span class="kt">var</span> <span class="n">responseJson</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">products</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">responseSize</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">responseJson</span><span class="p">);</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Histogram</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ResponseSizeHistogram</span><span class="p">,</span> <span class="n">responseSize</span><span class="p">);</span>

                <span class="c1">// Update API health score (100 for successful operations)</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">products</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Track server errors</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ServerErrorCounter</span><span class="p">);</span>
                
                <span class="c1">// Update API health score on error</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// GET: api/Product/5</span>
        <span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">GetProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Start timing the operation</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Timer</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">GetProductByIdTimer</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Increment API call counter</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">GetByProductIdCounter</span><span class="p">);</span>
                
                <span class="c1">// Track product views for business analytics</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ProductViewsCounter</span><span class="p">);</span>

                <span class="kt">var</span> <span class="n">product</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetByIdAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
                
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Track not found errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ProductNotFoundCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for client errors (75 - not as bad as server errors)</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">75</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
                <span class="p">}</span>

                <span class="c1">// Track response size</span>
                <span class="kt">var</span> <span class="n">responseJson</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">responseSize</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">responseJson</span><span class="p">);</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Histogram</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ResponseSizeHistogram</span><span class="p">,</span> <span class="n">responseSize</span><span class="p">);</span>

                <span class="c1">// Update API health score for successful operations</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Track server errors</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ServerErrorCounter</span><span class="p">);</span>
                
                <span class="c1">// Update API health score on server error</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// POST: api/Product</span>
        <span class="p">[</span><span class="n">HttpPost</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;&gt;</span> <span class="nf">CreateProduct</span><span class="p">(</span><span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Start timing the operation</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Timer</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">CreateProductTimer</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Increment API call counter</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">CreateProductCounter</span><span class="p">);</span>

                <span class="c1">// Track request size</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">requestJson</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
                    <span class="kt">var</span> <span class="n">requestSize</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">requestJson</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Histogram</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">RequestSizeHistogram</span><span class="p">,</span> <span class="n">requestSize</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Validation checks</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span> <span class="p">||</span> <span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">))</span>
                <span class="p">{</span>
                    <span class="c1">// Track validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ValidationErrorCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">80</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">(</span><span class="s">"Product name is required"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="kt">var</span> <span class="n">createdProduct</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">CreateAsync</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
                
                <span class="c1">// Track business metrics</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ProductCreatedTodayCounter</span><span class="p">);</span>
                
                <span class="c1">// Update total products count</span>
                <span class="kt">var</span> <span class="n">allProducts</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetAllAsync</span><span class="p">();</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">TotalProductsGauge</span><span class="p">,</span> <span class="n">allProducts</span><span class="p">.</span><span class="nf">Count</span><span class="p">());</span>
                
                <span class="c1">// Update average price</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">allProducts</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">averagePrice</span> <span class="p">=</span> <span class="n">allProducts</span><span class="p">.</span><span class="nf">Average</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">AverageProductPriceGauge</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">averagePrice</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Track response size</span>
                <span class="kt">var</span> <span class="n">responseJson</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">createdProduct</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">responseSize</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">responseJson</span><span class="p">);</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Histogram</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ResponseSizeHistogram</span><span class="p">,</span> <span class="n">responseSize</span><span class="p">);</span>

                <span class="c1">// Update API health score for successful operations</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetProduct</span><span class="p">),</span> <span class="k">new</span> <span class="p">{</span> <span class="n">id</span> <span class="p">=</span> <span class="n">createdProduct</span><span class="p">.</span><span class="n">Id</span> <span class="p">},</span> <span class="n">createdProduct</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Track server errors</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ServerErrorCounter</span><span class="p">);</span>
                
                <span class="c1">// Update API health score on server error</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// PUT: api/Product/5</span>
        <span class="p">[</span><span class="nf">HttpPut</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">UpdateProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">Product</span> <span class="n">product</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Start timing the operation</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Timer</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">UpdateProductTimer</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Increment API call counter</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">UpdateProductCounter</span><span class="p">);</span>

                <span class="c1">// Track request size</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">requestJson</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
                    <span class="kt">var</span> <span class="n">requestSize</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetByteCount</span><span class="p">(</span><span class="n">requestJson</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Histogram</span><span class="p">.</span><span class="nf">Update</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">RequestSizeHistogram</span><span class="p">,</span> <span class="n">requestSize</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Validation checks</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="p">==</span> <span class="k">null</span> <span class="p">||</span> <span class="n">id</span> <span class="p">!=</span> <span class="n">product</span><span class="p">.</span><span class="n">Id</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Track validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ValidationErrorCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">80</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">(</span><span class="s">"Product ID mismatch"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="n">Name</span><span class="p">))</span>
                <span class="p">{</span>
                    <span class="c1">// Track validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ValidationErrorCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for validation errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">80</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">BadRequest</span><span class="p">(</span><span class="s">"Product name is required"</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="kt">var</span> <span class="n">existingProduct</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetByIdAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">existingProduct</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Track not found errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ProductNotFoundCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for client errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">75</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
                <span class="p">}</span>

                <span class="kt">var</span> <span class="n">updatedProduct</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">UpdateAsync</span><span class="p">(</span><span class="n">product</span><span class="p">);</span>
                
                <span class="c1">// Update business metrics after successful update</span>
                <span class="kt">var</span> <span class="n">allProducts</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetAllAsync</span><span class="p">();</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">allProducts</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">averagePrice</span> <span class="p">=</span> <span class="n">allProducts</span><span class="p">.</span><span class="nf">Average</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">AverageProductPriceGauge</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">averagePrice</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Update API health score for successful operations</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">updatedProduct</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Track server errors</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ServerErrorCounter</span><span class="p">);</span>
                
                <span class="c1">// Update API health score on server error</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// DELETE: api/Product/5</span>
        <span class="p">[</span><span class="nf">HttpDelete</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
        <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">DeleteProduct</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// Start timing the operation</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">timer</span> <span class="p">=</span> <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Timer</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">DeleteProductTimer</span><span class="p">);</span>
            
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="c1">// Increment API call counter</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">DeleteProductCounter</span><span class="p">);</span>

                <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">DeleteAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
                
                <span class="k">if</span> <span class="p">(!</span><span class="n">result</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="c1">// Track not found errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ProductNotFoundCounter</span><span class="p">);</span>
                    
                    <span class="c1">// Update API health score for client errors</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">75</span><span class="p">);</span>
                    
                    <span class="k">return</span> <span class="nf">NotFound</span><span class="p">();</span>
                <span class="p">}</span>

                <span class="c1">// Update business metrics after successful deletion</span>
                <span class="kt">var</span> <span class="n">allProducts</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_productRepository</span><span class="p">.</span><span class="nf">GetAllAsync</span><span class="p">();</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">TotalProductsGauge</span><span class="p">,</span> <span class="n">allProducts</span><span class="p">.</span><span class="nf">Count</span><span class="p">());</span>
                
                <span class="c1">// Update average price</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">allProducts</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">averagePrice</span> <span class="p">=</span> <span class="n">allProducts</span><span class="p">.</span><span class="nf">Average</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">Price</span><span class="p">);</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">AverageProductPriceGauge</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">averagePrice</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">else</span>
                <span class="p">{</span>
                    <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">AverageProductPriceGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="c1">// Update API health score for successful operations</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>

                <span class="k">return</span> <span class="nf">NoContent</span><span class="p">();</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="c1">// Track server errors</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Counter</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ServerErrorCounter</span><span class="p">);</span>
                
                <span class="c1">// Update API health score on server error</span>
                <span class="n">_metrics</span><span class="p">.</span><span class="n">Measure</span><span class="p">.</span><span class="n">Gauge</span><span class="p">.</span><span class="nf">SetValue</span><span class="p">(</span><span class="n">MetricsRegistry</span><span class="p">.</span><span class="n">ApiHealthScoreGauge</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
                
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Step 8: Configure Program.cs for Metrics, Middleware, and Database</strong>
Update the <code class="language-plaintext highlighter-rouge">Program.cs</code> file to configure services, middleware, and database context.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">EcommerceApi.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">EcommerceApi.Repositories</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.AspNetCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">App.Metrics.Formatters.Prometheus</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Configure metrics with Prometheus formatters and web tracking</span>
<span class="c1">// This enables comprehensive HTTP request monitoring and Prometheus integration for Grafana</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Host</span><span class="p">.</span><span class="nf">UseMetricsWebTracking</span><span class="p">().</span><span class="nf">UseMetrics</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="n">EndpointOptions</span> <span class="p">=</span> <span class="n">endpointoptions</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="c1">// Configure Prometheus text formatter for /metrics-text endpoint (human readable)</span>
        <span class="n">endpointoptions</span><span class="p">.</span><span class="n">MetricsTextEndpointOutputFormatter</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricsPrometheusTextOutputFormatter</span><span class="p">();</span>
        <span class="c1">// Configure Prometheus protobuf formatter for /metrics endpoint (efficient binary format)</span>
        <span class="n">endpointoptions</span><span class="p">.</span><span class="n">MetricsEndpointOutputFormatter</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MetricsPrometheusProtobufOutputFormatter</span><span class="p">();</span>
        <span class="c1">// Disable environment info endpoint to reduce security exposure</span>
        <span class="n">endpointoptions</span><span class="p">.</span><span class="n">EnvironmentInfoEndpointEnabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
    <span class="p">};</span>
<span class="p">}).</span><span class="nf">UseMetricsWebTracking</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="c1">// Enable Apdex (Application Performance Index) tracking for user satisfaction monitoring</span>
    <span class="c1">// Apdex measures response time satisfaction: Satisfied/Tolerating/Frustrated requests</span>
    <span class="n">options</span><span class="p">.</span><span class="n">ApdexTrackingEnabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
    <span class="c1">// Set Apdex T threshold to 0.1 seconds (100ms) - responses faster than this are "satisfied"</span>
    <span class="n">options</span><span class="p">.</span><span class="n">ApdexTSeconds</span> <span class="p">=</span> <span class="m">0.1</span><span class="p">;</span>
<span class="p">});</span>

<span class="c1">// Add services to the container.</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>

<span class="c1">// Add Swagger services</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddEndpointsApiExplorer</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerDoc</span><span class="p">(</span><span class="s">"v1"</span><span class="p">,</span> <span class="k">new</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">OpenApi</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">OpenApiInfo</span> 
    <span class="p">{</span> 
        <span class="n">Title</span> <span class="p">=</span> <span class="s">"Ecommerce API"</span><span class="p">,</span> 
        <span class="n">Version</span> <span class="p">=</span> <span class="s">"v1"</span><span class="p">,</span>
        <span class="n">Description</span> <span class="p">=</span> <span class="s">"A simple ecommerce API for product management"</span>
    <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// Configure Entity Framework with In-Memory Database</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">EcommerceDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">UseInMemoryDatabase</span><span class="p">(</span><span class="s">"EcommerceInMemoryDb"</span><span class="p">));</span>

<span class="c1">// Register Repository</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">IProductRepository</span><span class="p">,</span> <span class="n">ProductRepository</span><span class="p">&gt;();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="c1">// Ensure the database is created and seeded</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">EcommerceDbContext</span><span class="p">&gt;();</span>
    <span class="n">context</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">EnsureCreated</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Configure the HTTP request pipeline.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
<span class="p">{</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwagger</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">c</span><span class="p">.</span><span class="nf">SwaggerEndpoint</span><span class="p">(</span><span class="s">"/swagger/v1/swagger.json"</span><span class="p">,</span> <span class="s">"Ecommerce API v1"</span><span class="p">);</span>
        <span class="n">c</span><span class="p">.</span><span class="n">RoutePrefix</span> <span class="p">=</span> <span class="s">"swagger"</span><span class="p">;</span>
    <span class="p">});</span>
<span class="p">}</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>

<span class="c1">// Enable metrics collection middleware - automatically tracks HTTP requests/responses</span>
<span class="c1">// This captures timing, status codes, and other HTTP metrics for Grafana monitoring</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseMetricsAllMiddleware</span><span class="p">();</span>
<span class="c1">// Expose metrics endpoints (/metrics, /metrics-text, /ping) for Prometheus scraping</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseMetricsAllEndpoints</span><span class="p">();</span>

<span class="c1">// Map controllers</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>

<p>Now you can run your .NET Core API project, and it will expose metrics at the <code class="language-plaintext highlighter-rouge">/metrics</code> endpoint in a Prometheus-compatible format. You can then configure Prometheus to scrape this endpoint and set up Grafana dashboards to visualize the collected metrics.</p>

<p>Example Endpoint:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Swagger UI: http://localhost:5262/swagger
Metrics Endpoints (for Grafana):
http://localhost:5262/metrics - Prometheus protobuf format
http://localhost:5262/metrics-text - Prometheus text format
http://localhost:5262/ping - Health check
</code></pre></div></div>

<p><strong>Step 9: Setting Up Prometheus</strong></p>

<p>To set up Prometheus to scrape metrics from your .NET Core API, follow these steps:</p>
<ol>
  <li><strong>Download and Install Prometheus</strong>: You can download Prometheus from the <a href="https://prometheus.io/download/">official website</a>.</li>
</ol>

<ul>
  <li>Download the latest stable version (ex. prometheus-3.8.1.windows-amd64.zip) and extract the files to a directory of your choice.</li>
</ul>

<ol>
  <li><strong>Configure Prometheus</strong>: After extracting Prometheus, navigate to the directory and you will find a <code class="language-plaintext highlighter-rouge">prometheus.yml</code> file. Modify this file to add your .NET Core API as a scrape target.</li>
</ol>

<p>Create a <code class="language-plaintext highlighter-rouge">prometheus.yml</code> configuration file with the following content:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">global</span><span class="pi">:</span>
  <span class="na">scrape_interval</span><span class="pi">:</span> <span class="s">15s</span>
<span class="na">scrape_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">job_name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">ecommerce_api'</span>
    <span class="na">static_configs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">targets</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">localhost:5262'</span><span class="pi">]</span>  <span class="c1"># Replace with your API's host and port</span>
</code></pre></div></div>

<ul>
  <li>Save and close the file.</li>
</ul>

<ol>
  <li><strong>Run Prometheus</strong>: To start Prometheus with the updated configuration, run the following command in the terminal from the directory where Prometheus is located:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.<span class="se">\p</span>rometheus.exe <span class="nt">--config</span>.file<span class="o">=</span>prometheus.yml
</code></pre></div></div>

<ul>
  <li>Keep the Prometheus server running to allow it to scrape metrics from your .NET Core API.</li>
  <li>Now you can access the Prometheus web interface at <code class="language-plaintext highlighter-rouge">http://localhost:9090</code> to query and visualize the metrics being collected.</li>
</ul>

<p><img src="/assets/images/posts/2025/prometheus_01.png" alt="Prometheus Dashboard" /></p>

<p>As you can see that Prometheus is configured, you can try with different query to verify or move to next step. For example, you can use the following query to see the total number of requests to the <code class="language-plaintext highlighter-rouge">GetAllProducts</code> endpoint:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sum(rate(get_all_products_total[1m]))
</code></pre></div></div>

<p>And you can use the following query to see the average response time for the <code class="language-plaintext highlighter-rouge">GetProductById</code> endpoint:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avg(rate(get_product_by_id_duration_sum[1m])) / avg(rate(get_product_by_id_duration_count[1m]))
</code></pre></div></div>

<p><strong>Step 10: Setting Up Grafana</strong>
To set up Grafana to visualize the metrics collected by Prometheus, follow these steps:</p>

<ol>
  <li><strong>Download and Install Grafana</strong>: You can download Grafana from the <a href="https://grafana.com/grafana/download">official website</a>.
    <ul>
      <li>Download the latest stable version which match with your OS  (ex. grafana-enterprise_12.3.1_20271043721_windows_amd64.tar.gz) and extract the files to a directory of your choice.</li>
      <li>Run the Grafana server by executing <code class="language-plaintext highlighter-rouge">grafana-server.exe</code> in bin folder from the extracted directory.</li>
      <li>Keep the Grafana server running to allow access to the web interface.</li>
      <li>Now you can access the Grafana web interface at <code class="language-plaintext highlighter-rouge">http://localhost:3000</code>.</li>
    </ul>
  </li>
</ol>

<p><img src="/assets/images/posts/2025/grafana_login_01.png" alt="" /></p>

<ol>
  <li><strong>Add Prometheus as a Data Source</strong>:
    <ul>
      <li>Open Grafana in your web browser (default is <code class="language-plaintext highlighter-rouge">http://localhost:3000</code>).</li>
      <li>Log in with the default credentials (<code class="language-plaintext highlighter-rouge">admin</code>/<code class="language-plaintext highlighter-rouge">admin</code>).</li>
      <li>You can either change the password or skip to keep the default user credential.</li>
      <li>Click on Gear Icon at the top left side and go to “Connections” &gt; “Data Sources” and click “Add data source”.</li>
      <li>Or, from the dashboard home page, click on “Add data source”.</li>
      <li>Select “Prometheus” from the list of data sources.</li>
      <li>Set the URL to <code class="language-plaintext highlighter-rouge">http://localhost:9090</code> (or wherever your Prometheus server is running) and click “Save &amp; Test”.</li>
    </ul>
  </li>
</ol>

<p><img src="/assets/images/posts/2025/prometheus_config_in_grafana_01.png" alt="" /></p>

<p><strong>Step 11: Configure a Custom Dashboards in Grafana</strong></p>

<p><strong>Step i: Create a New Dashboard</strong></p>
<ol>
  <li>Click the <code class="language-plaintext highlighter-rouge">+</code> icon at the top right corner</li>
  <li>Select “Create” → “Dashboard”</li>
  <li>Click “Add visualization”</li>
  <li>Select your “Prometheus” data source</li>
</ol>

<p><strong>Step ii: Add Panels for Your API Metrics</strong></p>

<p>Based on your metrics output from <code class="language-plaintext highlighter-rouge">http://localhost:5262/metrics-text</code>, create these panels:</p>

<p><img src="/assets/images/posts/2025/grafana_panel_setup01.png" alt="" /></p>

<p><strong>Panel 1: Total HTTP Requests</strong></p>
<ul>
  <li><strong>Title</strong>: “Total HTTP Requests”</li>
  <li><strong>Query</strong>: <code class="language-plaintext highlighter-rouge">application_httprequests_transactions_count</code></li>
  <li><strong>Panel Type</strong>: Stat</li>
  <li><strong>Description</strong>: Shows total number of requests processed</li>
  <li><strong>Note</strong>: Select Code option to write query</li>
  <li>Save the panel and save the dashboard. In my case - “Mahedee’s Dashboard”</li>
  <li>Now from your dashboard -&gt; Click on Add -&gt; Visualization and the following panels.</li>
</ul>

<p><strong>Panel 2: Request Rate</strong></p>
<ul>
  <li><strong>Title</strong>: “Requests per Second”</li>
  <li><strong>Query</strong>: <code class="language-plaintext highlighter-rouge">rate(application_httprequests_transactions_count[5m])</code></li>
  <li><strong>Panel Type</strong>: Time Series</li>
  <li><strong>Unit</strong>: requests/sec</li>
</ul>

<p><strong>Panel 3: Average Response Time</strong></p>
<ul>
  <li><strong>Title</strong>: “Average Response Time”</li>
  <li><strong>Query</strong>: <code class="language-plaintext highlighter-rouge">rate(application_httprequests_transactions_sum[5m]) / rate(application_httprequests_transactions_count[5m])</code></li>
  <li><strong>Panel Type</strong>: Stat</li>
  <li><strong>Unit</strong>: seconds</li>
</ul>

<p><strong>Panel 4: Response Time Percentiles</strong></p>
<ul>
  <li><strong>Title</strong>: “Response Time Distribution”</li>
  <li><strong>Queries</strong>:
    <ul>
      <li>Alias “50th”: <code class="language-plaintext highlighter-rouge">application_httprequests_transactions{quantile="0.5"}</code></li>
      <li>Alias “75th”: <code class="language-plaintext highlighter-rouge">application_httprequests_transactions{quantile="0.75"}</code></li>
      <li>Alias “95th”: <code class="language-plaintext highlighter-rouge">application_httprequests_transactions{quantile="0.95"}</code></li>
      <li>Alias “99th”: <code class="language-plaintext highlighter-rouge">application_httprequests_transactions{quantile="0.99"}</code></li>
    </ul>
  </li>
  <li><strong>Panel Type</strong>: Time Series</li>
  <li><strong>Unit</strong>: seconds</li>
</ul>

<p><strong>Panel 5: Apdex Score</strong></p>
<ul>
  <li><strong>Title</strong>: “Application Performance Index (User Satisfaction)”</li>
  <li><strong>Query</strong>: <code class="language-plaintext highlighter-rouge">application_httprequests_apdex</code></li>
  <li><strong>Panel Type</strong>: Stat</li>
  <li><strong>Unit</strong>: percent (0-100)</li>
  <li><strong>Thresholds</strong>: Green &gt; 85, Yellow &gt; 70, Red &lt; 70</li>
</ul>

<p><strong>Panel 6: Active Requests</strong></p>
<ul>
  <li><strong>Title</strong>: “Currently Active Requests”</li>
  <li><strong>Query</strong>: <code class="language-plaintext highlighter-rouge">application_httprequests_active</code></li>
  <li><strong>Panel Type</strong>: Stat</li>
  <li><strong>Unit</strong>: short</li>
</ul>

<p><strong>Note: You can add more panels based on the metrics you have implemented in your .NET Core API. Customize the dashboard layout and styles as per your preference.</strong></p>

<p><strong>Optional Steps: Generate Test Data</strong></p>

<p>Before you can see data in your dashboard, generate some API traffic. You can create a shell script using the following commands and run it in your terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Generate sustained traffic to create visible metrics</span>
<span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>1..100<span class="o">}</span><span class="p">;</span> <span class="k">do
  </span>curl http://localhost:5262/api/Product
  curl http://localhost:5262/api/Product/1
  curl http://localhost:5262/api/Product/2
  <span class="nb">sleep </span>0.2
<span class="k">done</span>

<span class="c"># Create some products</span>
curl <span class="nt">-X</span> POST http://localhost:5262/api/Product <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"name":"Dashboard Test Product","description":"Testing dashboard","price":99.99,"stockQuantity":10}'</span>

<span class="c"># Test 404 errors</span>
curl http://localhost:5262/api/Product/999
curl http://localhost:5262/api/Product/888
</code></pre></div></div>

<p><strong>Step iii: View your dashboard</strong></p>
<ul>
  <li>After adding all the panels, save your dashboard. You should now see real-time metrics from your .NET Core API displayed in Grafana.</li>
</ul>

<p><img src="/assets/images/posts/2025/grafana_custom_dashboard01.png" alt="" /></p>

<p><strong>Step 11: Import Pre-Built Dashboards in Grafana</strong></p>

<ul>
  <li>Click on the “+” icon on the top right and select “Import Dashboard”.</li>
  <li>Let’s import a dashboard from Grafana’s dashboard repository. For example, you can use the dashboard ID <code class="language-plaintext highlighter-rouge">4334</code> for a Prometheus overview dashboard it is called - ‘App Metrics - Web Monitoring - Prometheus’.</li>
  <li>Visit <a href="https://grafana.com/grafana/dashboards">Grafana Dashboards</a> to find more dashboards. If you want to explore more dashboards, you can search for “.NET Core” or “App Metrics” to find relevant dashboards and you will get the dashboard ID from there.</li>
  <li>Enter <code class="language-plaintext highlighter-rouge">4334</code> in the “Import via grafana.com” field and click “Load”.</li>
  <li>Select the Prometheus data source you added earlier and click “Import”.</li>
  <li>Save the dashboard.</li>
  <li>You will see the following dashboard:</li>
</ul>

<p><img src="/assets/images/posts/2025/grafana_dashboard_4334.png" alt="" /></p>

<p><strong>Conclusion</strong>
Implementing metrics and dashboards for your .NET Core APIs using Prometheus and Grafana significantly enhances your application’s observability. By following the steps outlined in this guide, you can effectively monitor your API’s performance, track key metrics, and visualize data through customizable dashboards. This setup not only helps in identifying performance bottlenecks but also aids in proactive issue resolution, ensuring a robust and reliable application.</p>

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/master/grafana-dotnet-core">Source code</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term=".NET Core" /><category term="ASP.NET Core" /><category term="DevOps" /><category term="Monitoring" /><category term="dotnet" /><category term="aspnetcore" /><category term="webapi" /><category term="prometheus" /><category term="grafana" /><category term="monitoring" /><category term="metrics" /><category term="observability" /><category term="devops" /><category term="configuration" /><summary type="html"><![CDATA[Step-by-step tutorial to implement metrics and dashboards for .NET Core APIs using Prometheus and Grafana for effective monitoring.]]></summary></entry><entry><title type="html">Open-Close Principle in C# with Real-World Examples</title><link href="https://mahedee.net/open-close-principle/" rel="alternate" type="text/html" title="Open-Close Principle in C# with Real-World Examples" /><published>2025-12-05T00:00:00-05:00</published><updated>2025-12-05T00:00:00-05:00</updated><id>https://mahedee.net/open-close-principle</id><content type="html" xml:base="https://mahedee.net/open-close-principle/"><![CDATA[<p>Open Close Principle is an Object Oriented Design principle. It is first introduced by Betrand Meyer in 1988. He says “Software entities (Class, module, function etc.) should be open for extension, but closed for modification”. An entity is “Open for extension” means that its behavior can be extended to accommodate new demand. The entity is “closed for modification” means that the existing source code of the module is not changed or minimum change when making enhancement. It is clear that if a system cannot accommodate change easily, its life cycle will end fast.</p>

<p>Sometimes code changes introduce heavy risk. At the time of changing, you must ensure that changes will not break the system. Sometimes it takes huge regression testing. This risk can be minimized if no changes are made to existing code.</p>

<p>So, our intention should be writing code in such a way that new functionality should be added with minimum changes or not changes in the existing code.  It should be done in a way to allow the adding of new functionality as new classes, keeping as much as possible existing code unchanged. The major advantages of “open close principle” is that it undergo changes and its value will be tremendous. It required almost no regression testing.</p>

<p>Let’s introduce open close principle with an example. Suppose, in our application, we need a “Area calculator” which calculate area of rectangle. However, in this occasion we just create a class AreaCalculator then there will be a method RectangleArea in this class which just calculates area of rectangle. It works fine. In the middle of the application development, we need to calculate area of Triangle and Circle. In this occasion, what should we do? We just add another two method TriangleArea and CircleArea and can do the job. But several problems will arise here – for each new shape you have to add new unit of code. Developer must have to know the logic to calculate area of new shape. Adding a new shape might effect in existing functionalities. So, it will take huge cost of regression testing. This is actually violate, open close principle.</p>

<p>We implement the same problem abide by open close principle by the following way. Here Rectangle, Triangle and Circle class inherit the Shape class and implement CalculateArea Method. In this way, if you need to calculate area of x shape just add a class of x and then implement shape and calculate area of x without modifying exiting code.</p>

<h2 id="implementation-the-right-way">Implementation: The Right Way</h2>

<h3 id="class-diagram">Class diagram:</h3>

<p><img src="/assets/images/posts/2025/open-close-principle-class-diagram.png" alt="" /></p>

<h3 id="benefits-of-this-design">Benefits of This Design:</h3>
<ul>
  <li>✅ <strong>Extensible</strong>: New shapes can be added without modifying existing code</li>
  <li>✅ <strong>Maintainable</strong>: Each shape encapsulates its own area calculation logic</li>
  <li>✅ <strong>Testable</strong>: Each shape can be tested independently</li>
  <li>✅ <strong>Follows SRP</strong>: Each class has a single responsibility</li>
</ul>

<h3 id="step-by-step-implementation">Step-by-Step Implementation</h3>

<h4 id="step-1-create-the-abstract-shape-base-class">Step 1: Create the Abstract Shape Base Class</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Abstract base class for all geometric shapes</span>
    <span class="c1">/// Defines the contract that all shapes must follow</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">abstract</span> <span class="k">class</span> <span class="nc">Shape</span>
    <span class="p">{</span>
        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Calculate the area of the shape</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;returns&gt;The area as a double value&lt;/returns&gt;</span>
        <span class="k">public</span> <span class="k">abstract</span> <span class="kt">double</span> <span class="nf">CalculateArea</span><span class="p">();</span>
        
        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Get the name of the shape (optional: for display purposes)</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="k">public</span> <span class="k">virtual</span> <span class="kt">string</span> <span class="nf">GetShapeName</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nf">GetType</span><span class="p">().</span><span class="n">Name</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="step-2-implement-rectangle-class">Step 2: Implement Rectangle Class</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Represents a rectangle shape</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Rectangle</span> <span class="p">:</span> <span class="n">Shape</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">Height</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">Width</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="nf">Rectangle</span><span class="p">(</span><span class="kt">double</span> <span class="n">height</span><span class="p">,</span> <span class="kt">double</span> <span class="n">width</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">height</span> <span class="p">&lt;=</span> <span class="m">0</span> <span class="p">||</span> <span class="n">width</span> <span class="p">&lt;=</span> <span class="m">0</span><span class="p">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Height and width must be positive values."</span><span class="p">);</span>
                
            <span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">;</span>
            <span class="n">Width</span> <span class="p">=</span> <span class="n">width</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">double</span> <span class="nf">CalculateArea</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">Height</span> <span class="p">*</span> <span class="n">Width</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">string</span> <span class="nf">ToString</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">$"Rectangle(Width: </span><span class="p">{</span><span class="n">Width</span><span class="p">}</span><span class="s">, Height: </span><span class="p">{</span><span class="n">Height</span><span class="p">}</span><span class="s">)"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="step-3-implement-triangle-class">Step 3: Implement Triangle Class</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Represents a triangle shape</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Triangle</span> <span class="p">:</span> <span class="n">Shape</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">Base</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">Height</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="nf">Triangle</span><span class="p">(</span><span class="kt">double</span> <span class="n">baseLength</span><span class="p">,</span> <span class="kt">double</span> <span class="n">height</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">baseLength</span> <span class="p">&lt;=</span> <span class="m">0</span> <span class="p">||</span> <span class="n">height</span> <span class="p">&lt;=</span> <span class="m">0</span><span class="p">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Base and height must be positive values."</span><span class="p">);</span>
                
            <span class="n">Base</span> <span class="p">=</span> <span class="n">baseLength</span><span class="p">;</span>
            <span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">double</span> <span class="nf">CalculateArea</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="m">0.5</span> <span class="p">*</span> <span class="n">Base</span> <span class="p">*</span> <span class="n">Height</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">string</span> <span class="nf">ToString</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">$"Triangle(Base: </span><span class="p">{</span><span class="n">Base</span><span class="p">}</span><span class="s">, Height: </span><span class="p">{</span><span class="n">Height</span><span class="p">}</span><span class="s">)"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="step-4-implement-circle-class">Step 4: Implement Circle Class</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Represents a circle shape</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Circle</span> <span class="p">:</span> <span class="n">Shape</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">Radius</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="nf">Circle</span><span class="p">(</span><span class="kt">double</span> <span class="n">radius</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">radius</span> <span class="p">&lt;=</span> <span class="m">0</span><span class="p">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Radius must be a positive value."</span><span class="p">);</span>
                
            <span class="n">Radius</span> <span class="p">=</span> <span class="n">radius</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">double</span> <span class="nf">CalculateArea</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">Radius</span> <span class="p">*</span> <span class="n">Radius</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">string</span> <span class="nf">ToString</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">$"Circle(Radius: </span><span class="p">{</span><span class="n">Radius</span><span class="p">}</span><span class="s">)"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="demonstrating-extensibility">Demonstrating Extensibility</h2>

<h3 id="adding-a-new-shape-without-modifying-existing-code">Adding a New Shape Without Modifying Existing Code</h3>

<p>Let’s add a Pentagon to demonstrate how easy it is to extend our system:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Represents a regular pentagon shape</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Pentagon</span> <span class="p">:</span> <span class="n">Shape</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">double</span> <span class="n">SideLength</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
        
        <span class="k">public</span> <span class="nf">Pentagon</span><span class="p">(</span><span class="kt">double</span> <span class="n">sideLength</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">sideLength</span> <span class="p">&lt;=</span> <span class="m">0</span><span class="p">)</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Side length must be a positive value."</span><span class="p">);</span>
                
            <span class="n">SideLength</span> <span class="p">=</span> <span class="n">sideLength</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">double</span> <span class="nf">CalculateArea</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="c1">// Formula for regular pentagon: (1/4) * √(25 + 10√5) * s²</span>
            <span class="kt">double</span> <span class="n">coefficient</span> <span class="p">=</span> <span class="m">0.25</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="m">25</span> <span class="p">+</span> <span class="m">10</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="m">5</span><span class="p">));</span>
            <span class="k">return</span> <span class="n">coefficient</span> <span class="p">*</span> <span class="n">SideLength</span> <span class="p">*</span> <span class="n">SideLength</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">public</span> <span class="k">override</span> <span class="kt">string</span> <span class="nf">ToString</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="s">$"Pentagon(SideLength: </span><span class="p">{</span><span class="n">SideLength</span><span class="p">}</span><span class="s">)"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="step-5-using-the-shapes-client-code">Step 5: Using the Shapes (Client Code)</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">OCP</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"=== Area Calculator using Open-Close Principle ==="</span><span class="p">);</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">();</span>
            
            <span class="c1">// Create a list of different shapes</span>
            <span class="n">List</span><span class="p">&lt;</span><span class="n">Shape</span><span class="p">&gt;</span> <span class="n">shapes</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Shape</span><span class="p">&gt;</span>
            <span class="p">{</span>
                <span class="k">new</span> <span class="nf">Rectangle</span><span class="p">(</span><span class="m">20</span><span class="p">,</span> <span class="m">30</span><span class="p">),</span>
                <span class="k">new</span> <span class="nf">Triangle</span><span class="p">(</span><span class="m">15</span><span class="p">,</span> <span class="m">25</span><span class="p">),</span>
                <span class="k">new</span> <span class="nf">Circle</span><span class="p">(</span><span class="m">7</span><span class="p">),</span>
                <span class="k">new</span> <span class="nf">Pentagon</span><span class="p">(</span><span class="m">10</span><span class="p">)</span> <span class="c1">// New shape added without modifying existing code!</span>
            <span class="p">};</span>
            
            <span class="c1">// Calculate areas using polymorphism</span>
            <span class="k">foreach</span> <span class="p">(</span><span class="n">Shape</span> <span class="n">shape</span> <span class="k">in</span> <span class="n">shapes</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="k">try</span>
                <span class="p">{</span>
                    <span class="kt">double</span> <span class="n">area</span> <span class="p">=</span> <span class="n">shape</span><span class="p">.</span><span class="nf">CalculateArea</span><span class="p">();</span>
                    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">shape</span><span class="p">}</span><span class="s"> =&gt; Area: </span><span class="p">{</span><span class="n">area</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Error calculating area for </span><span class="p">{</span><span class="n">shape</span><span class="p">.</span><span class="nf">GetShapeName</span><span class="p">()}</span><span class="s">: </span><span class="p">{</span><span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
            
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">();</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Press any key to exit..."</span><span class="p">);</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">ReadKey</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="sample-output">Sample Output</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=== Area Calculator using Open-Close Principle ===

Rectangle(Width: 20, Height: 30) =&gt; Area: 600.00
Triangle(Base: 15, Height: 25) =&gt; Area: 187.50
Circle(Radius: 7) =&gt; Area: 153.94
Pentagon(SideLength: 10) =&gt; Area: 172.05

Press any key to exit...
</code></pre></div></div>

<h2 id="advanced-example-area-calculator-with-strategy-pattern">Advanced Example: Area Calculator with Strategy Pattern</h2>

<p>For even more flexibility, you can combine OCP with other patterns:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">AreaCalculatorService</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">ProcessShapes</span><span class="p">(</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">Shape</span><span class="p">&gt;</span> <span class="n">shapes</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">shape</span> <span class="k">in</span> <span class="n">shapes</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">area</span> <span class="p">=</span> <span class="n">shape</span><span class="p">.</span><span class="nf">CalculateArea</span><span class="p">();</span>
            <span class="kt">var</span> <span class="n">perimeter</span> <span class="p">=</span> <span class="nf">CalculatePerimeter</span><span class="p">(</span><span class="n">shape</span><span class="p">);</span> <span class="c1">// Could be another extensible method</span>
            
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">shape</span><span class="p">.</span><span class="nf">GetShapeName</span><span class="p">()}</span><span class="s">: Area = </span><span class="p">{</span><span class="n">area</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">, Perimeter = </span><span class="p">{</span><span class="n">perimeter</span><span class="p">:</span><span class="n">F2</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="k">private</span> <span class="kt">double</span> <span class="nf">CalculatePerimeter</span><span class="p">(</span><span class="n">Shape</span> <span class="n">shape</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// This could also follow OCP by having each shape implement IPerimeterCalculable</span>
        <span class="k">return</span> <span class="n">shape</span> <span class="k">switch</span>
        <span class="p">{</span>
            <span class="n">Rectangle</span> <span class="n">r</span> <span class="p">=&gt;</span> <span class="m">2</span> <span class="p">*</span> <span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">Width</span> <span class="p">+</span> <span class="n">r</span><span class="p">.</span><span class="n">Height</span><span class="p">),</span>
            <span class="n">Circle</span> <span class="n">c</span> <span class="p">=&gt;</span> <span class="m">2</span> <span class="p">*</span> <span class="n">Math</span><span class="p">.</span><span class="n">PI</span> <span class="p">*</span> <span class="n">c</span><span class="p">.</span><span class="n">Radius</span><span class="p">,</span>
            <span class="n">Triangle</span> <span class="n">t</span> <span class="p">=&gt;</span> <span class="nf">GetTrianglePerimeter</span><span class="p">(</span><span class="n">t</span><span class="p">),</span> <span class="c1">// Assuming we have this method</span>
            <span class="n">Pentagon</span> <span class="n">p</span> <span class="p">=&gt;</span> <span class="m">5</span> <span class="p">*</span> <span class="n">p</span><span class="p">.</span><span class="n">SideLength</span><span class="p">,</span>
            <span class="n">_</span> <span class="p">=&gt;</span> <span class="m">0</span>
        <span class="p">};</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="benefits-demonstrated">Benefits Demonstrated</h2>

<h3 id="-what-we-achieved">✅ <strong>What We Achieved</strong></h3>

<ol>
  <li><strong>Zero Modification</strong>: Adding Pentagon didn’t require changing any existing code</li>
  <li><strong>Easy Testing</strong>: Each shape can be tested independently</li>
  <li><strong>Clear Responsibilities</strong>: Each shape knows how to calculate its own area</li>
  <li><strong>Type Safety</strong>: Compile-time checking ensures all shapes implement required methods</li>
  <li><strong>Maintainability</strong>: Bug fixes in one shape don’t affect others</li>
</ol>

<h3 id="-how-to-add-more-shapes">🔄 <strong>How to Add More Shapes</strong></h3>

<p>To add any new shape (Hexagon, Octagon, etc.):</p>

<ol>
  <li>Create a new class inheriting from <code class="language-plaintext highlighter-rouge">Shape</code></li>
  <li>Implement the <code class="language-plaintext highlighter-rouge">CalculateArea()</code> method</li>
  <li>Add it to your shapes collection</li>
  <li><strong>That’s it!</strong> No existing code needs modification</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>The Open-Close Principle is fundamental to creating maintainable, extensible software. By designing our classes to be open for extension but closed for modification, we:</p>

<ul>
  <li>Reduce risk when adding new features</li>
  <li>Minimize regression testing</li>
  <li>Create more modular, testable code</li>
  <li>Enable easier collaboration among team members</li>
  <li>Build systems that gracefully accommodate future requirements</li>
</ul>

<p>Please share your thoughts and experiences with the Open-Close Principle in the comments below! Share in your networks if you found this guide helpful. Happy coding!</p>

<p><strong><a href="https://github.com/mahedee/code-sample02/tree/main/ocp">Source code</a></strong></p>]]></content><author><name>Mahedee Hasan</name></author><category term="OOP" /><category term="C#" /><category term="Design Principles" /><category term="SOLID" /><category term="csharp" /><category term="oop" /><category term="solid-principles" /><category term="open-closed-principle" /><category term="design-patterns" /><category term="software-architecture" /><category term="clean-code" /><category term="extensibility" /><summary type="html"><![CDATA[Learn how to implement the Open-Close Principle in C# with practical examples. Understand how to write code that's open for extension but closed for modification.]]></summary></entry><entry><title type="html">My VS Code Extensions for Daily Development Work</title><link href="https://mahedee.net/my-vs-code-extensions-for-daily-development/" rel="alternate" type="text/html" title="My VS Code Extensions for Daily Development Work" /><published>2025-11-05T00:00:00-05:00</published><updated>2025-11-05T00:00:00-05:00</updated><id>https://mahedee.net/my-vs-code-extensions-for-daily-development</id><content type="html" xml:base="https://mahedee.net/my-vs-code-extensions-for-daily-development/"><![CDATA[<p>As a developer, VS Code extensions are essential tools that enhance productivity and streamline the development workflow. Here’s a curated list of all the extensions I use daily, organized by category with explanations of why each one is valuable.</p>

<h2 id="-pro-tip-list-your-extensions">💡 Pro Tip: List Your Extensions</h2>
<p>To see which extensions you have installed, run this command in your terminal:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code <span class="nt">--list-extensions</span>
</code></pre></div></div>

<p>For detailed info with versions:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code <span class="nt">--list-extensions</span> <span class="nt">--show-versions</span>
</code></pre></div></div>

<h2 id="-ai--code-assistance">🤖 AI &amp; Code Assistance</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot">GitHub Copilot</a></strong><br />
AI-powered code completion that suggests entire lines or blocks of code. Essential for faster coding and learning new patterns.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat">GitHub Copilot Chat</a></strong><br />
Interactive AI assistant for code explanation, debugging, and refactoring suggestions directly in VS Code.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=continue.continue">Continue</a></strong><br />
Open-source AI code assistant that provides intelligent code completion and chat capabilities.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=bito.bito">Bito AI</a></strong><br />
AI-powered code assistant for generating, explaining, and optimizing code across multiple programming languages.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode">IntelliCode</a></strong><br />
Microsoft’s AI-assisted development with smart code completion based on best practices from thousands of open-source projects.</p>

<h2 id="-documentation--markdown">📝 Documentation &amp; Markdown</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced">Markdown Preview Enhanced</a></strong><br />
Advanced markdown preview with support for diagrams, math expressions, and export options. Perfect for README files and documentation.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=yzane.markdown-pdf">Markdown PDF</a></strong><br />
Convert markdown files to PDF, HTML, PNG, or JPEG formats. Great for sharing documentation in multiple formats.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=huntertran.auto-markdown-toc">Auto Markdown TOC</a></strong><br />
Automatically generates table of contents for markdown files, saving time when creating structured documents.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=mintlify.document">Mintlify Document</a></strong><br />
AI-powered documentation generator that creates detailed docstrings and comments for your code.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ChrisChinchilla.vscode-pandoc">Pandoc</a></strong><br />
Convert between different document formats using Pandoc directly from VS Code.</p>

<h2 id="-code-quality--formatting">🎨 Code Quality &amp; Formatting</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier - Code formatter</a></strong><br />
Automatic code formatting for consistent code style across JavaScript, TypeScript, CSS, and more.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments</a></strong><br />
Enhances code comments with color coding for TODO, FIXME, and other comment types.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck">ShellCheck</a></strong><br />
Linting for shell scripts to catch common errors and improve script quality.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=mkhl.shfmt">Shell Format</a></strong><br />
Formats shell scripts according to standard conventions for better readability.</p>

<h2 id="-language-support">🔧 Language Support</h2>

<h3 id="java--spring">Java &amp; Spring</h3>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack">Extension Pack for Java</a></strong><br />
Complete Java development suite including debugger, test runner, and project management.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=vmware.vscode-boot-dev-pack">Spring Boot Extension Pack</a></strong><br />
Tools for Spring Boot development including auto-completion and live application information.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-spring-boot-dashboard">Spring Boot Dashboard</a></strong><br />
Manage and monitor Spring Boot applications directly from VS Code.</p>

<h3 id="c--net">C# &amp; .NET</h3>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp">C#</a></strong><br />
Essential for C# development with IntelliSense, debugging, and project templates.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit">C# DevKit</a></strong><br />
Comprehensive C# development experience with enhanced project management and debugging.</p>

<h3 id="python">Python</h3>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python">Python</a></strong><br />
Complete Python development environment with debugging, linting, and IntelliSense.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance">Pylance</a></strong><br />
Fast, feature-rich language support for Python with type checking and auto-imports.</p>

<h3 id="javascript--react">JavaScript &amp; React</h3>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets">ES7+ React/Redux/React-Native snippets</a></strong><br />
Essential code snippets for React development, dramatically speeds up component creation.</p>

<h3 id="angular">Angular</h3>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=Angular.ng-template">Angular Language Service</a></strong><br />
Official Angular extension providing IntelliSense and error checking for Angular templates.</p>

<h2 id="-devops--containers">🐳 DevOps &amp; Containers</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker">Docker</a></strong><br />
Manage Docker containers and images directly from VS Code with syntax highlighting for Dockerfiles.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">Dev Containers</a></strong><br />
Develop inside Docker containers for consistent development environments.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl">Remote - WSL</a></strong><br />
Seamless development experience using Windows Subsystem for Linux.</p>

<h2 id="-git--version-control">🔍 Git &amp; Version Control</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens</a></strong><br />
Supercharges Git capabilities with blame annotations, commit history, and repository insights.</p>

<h2 id="-diagrams--visualization">📊 Diagrams &amp; Visualization</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio">Draw.io Integration</a></strong><br />
Create and edit diagrams directly in VS Code for documentation and system design.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml">PlantUML</a></strong><br />
Generate UML diagrams from text descriptions, perfect for technical documentation.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=tehpeng.diagramspreviewer">Diagrams Preview</a></strong><br />
Preview various diagram formats including Mermaid and PlantUML.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=pnp.polacode">Polacode</a></strong><br />
Create beautiful screenshots of your code for presentations and documentation.</p>

<h2 id="️-utilities--productivity">🛠️ Utilities &amp; Productivity</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client</a></strong><br />
Test REST APIs directly from VS Code without leaving the editor.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=AykutSarac.jsoncrack-vscode">JSON Crack</a></strong><br />
Visualize JSON data as interactive graphs for better understanding of complex structures.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=fabiospampinato.vscode-todo-plus">TODO+</a></strong><br />
Advanced TODO management with nested todos, tags, and time tracking.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=steoates.autoimport">Auto Import - ES6, TS, JSX, TSX</a></strong><br />
Automatically finds and adds import statements for TypeScript and JavaScript.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=chrmarti.regex">Regex</a></strong><br />
Test and debug regular expressions with real-time highlighting and explanation.</p>

<h2 id="️-database--cloud">🗄️ Database &amp; Cloud</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=Oracle.sql-developer">Oracle SQL Developer</a></strong><br />
Connect to and manage Oracle databases directly from VS Code.</p>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=dannysteenman.cdk-snippets">AWS CDK Snippets</a></strong><br />
Code snippets for AWS Cloud Development Kit to speed up infrastructure as code development.</p>

<h2 id="-shell--bash">💻 Shell &amp; Bash</h2>

<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=mads-hartmann.bash-ide-vscode">Bash IDE</a></strong><br />
Enhanced Bash scripting experience with syntax highlighting and IntelliSense.</p>

<hr />

<h2 id="summary">Summary</h2>

<p>These 50+ extensions transform VS Code into a powerful, multi-language IDE capable of handling everything from simple scripts to complex enterprise applications. The key is finding the right balance - install extensions that genuinely improve your workflow without overwhelming the editor.</p>

<p><strong>Remember</strong>: Extensions can slow down VS Code startup time, so regularly review and disable extensions you’re not actively using.</p>

<p><em>Have suggestions for other must-have extensions? Feel free to share in the comments below!</em></p>]]></content><author><name>Mahedee Hasan</name></author><category term="VS Code" /><category term="Tools" /><category term="Productivity" /><category term="DevOps" /><category term="AI" /><category term="vscode" /><category term="extensions" /><category term="tools" /><category term="productivity" /><category term="development" /><category term="ai" /><category term="docker" /><category term="github" /><category term="markdown" /><category term="python" /><category term="csharp" /><category term="java" /><category term="angular" /><category term="reviewed_v02" /><summary type="html"><![CDATA[A comprehensive list of VS Code extensions I use daily for development, productivity, and coding efficiency across multiple languages and frameworks.]]></summary></entry><entry><title type="html">How to Clone a GitHub Issue Using PowerShell and Shell Scripts</title><link href="https://mahedee.net/clone_a_github_issue_using_shellscript/" rel="alternate" type="text/html" title="How to Clone a GitHub Issue Using PowerShell and Shell Scripts" /><published>2025-11-04T00:00:00-05:00</published><updated>2025-11-04T00:00:00-05:00</updated><id>https://mahedee.net/clone_a_github_issue_using_shellscript</id><content type="html" xml:base="https://mahedee.net/clone_a_github_issue_using_shellscript/"><![CDATA[<h2 id="overview">Overview</h2>

<p>This comprehensive guide demonstrates how to clone GitHub issues using both PowerShell and shell scripts. The solution automatically creates a duplicate issue with the same title and body content, then closes the original issue with a reference to the newly created one.</p>

<!-- completed up to -->

<h2 id="what-is-issue-cloning">What is Issue Cloning?</h2>

<p>Issue cloning is the process of creating a new GitHub issue that duplicates the content of an existing issue. This is useful for:</p>
<ul>
  <li>Moving issues between repositories</li>
  <li>Creating templates from existing issues</li>
  <li>Archiving resolved issues while maintaining their content</li>
  <li>Reorganizing project workflows</li>
</ul>

<h2 id="prerequisites">Prerequisites</h2>

<h3 id="1-installing-github-cli">1. Installing GitHub CLI</h3>

<h4 id="on-windows-using-winget">On Windows using winget</h4>

<p>First, check if winget is available:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-Command</span><span class="w"> </span><span class="nx">winget</span><span class="w"> </span><span class="nt">-ErrorAction</span><span class="w"> </span><span class="nx">SilentlyContinue</span><span class="w">
</span></code></pre></div></div>

<p>If winget is not found, you can try adding it to your PATH:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="s2">";C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.27.440.0_x64__8wekyb3d8bbwe"</span><span class="w">
</span><span class="n">winget</span><span class="w"> </span><span class="nt">--version</span><span class="w">
</span></code></pre></div></div>

<p>Install GitHub CLI:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">GitHub.cli</span><span class="w">
</span></code></pre></div></div>

<h4 id="alternative-installation-methods">Alternative Installation Methods</h4>

<p><strong>Method 1: Manual Download</strong></p>
<ul>
  <li>Download from GitHub: https://github.com/cli/cli/releases</li>
  <li>Install the <code class="language-plaintext highlighter-rouge">.msi</code> file for Windows</li>
</ul>

<p><strong>Method 2: Using Chocolatey</strong></p>
<ul>
  <li>Chocolatey is a package manager for Windows. Install GitHub CLI with:</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">choco</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">gh</span><span class="w">
</span></code></pre></div></div>

<p><strong>Method 3: Direct Download</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$progressPreference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'silentlyContinue'</span><span class="w">
</span><span class="n">Invoke-WebRequest</span><span class="w"> </span><span class="nt">-Uri</span><span class="w"> </span><span class="nx">https://github.com/cli/cli/releases/latest/download/gh_windows_amd64.msi</span><span class="w"> </span><span class="nt">-OutFile</span><span class="w"> </span><span class="nx">gh_installer.msi</span><span class="w">
</span><span class="n">Start-Process</span><span class="w"> </span><span class="nx">msiexec.exe</span><span class="w"> </span><span class="nt">-ArgumentList</span><span class="w"> </span><span class="s2">"/i"</span><span class="p">,</span><span class="w"> </span><span class="s2">"gh_installer.msi"</span><span class="p">,</span><span class="w"> </span><span class="s2">"/quiet"</span><span class="w"> </span><span class="nt">-Wait</span><span class="w">
</span></code></pre></div></div>

<h3 id="2-add-github-cli-to-path">2. Add GitHub CLI to PATH</h3>

<p>After installation, add GitHub CLI to your system PATH:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="s2">";C:\Program Files\GitHub CLI"</span><span class="w">
</span><span class="n">gh</span><span class="w"> </span><span class="nt">--version</span><span class="w">
</span></code></pre></div></div>

<h3 id="3-github-authentication">3. GitHub Authentication</h3>

<p>Authenticate with GitHub:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gh auth login
</code></pre></div></div>

<p>Follow the prompts to authenticate using your browser or personal access token.</p>

<h2 id="solution-overview">Solution Overview</h2>

<p>We’ve created two scripts that provide the same functionality:</p>
<ol>
  <li><strong>PowerShell Script</strong> (<code class="language-plaintext highlighter-rouge">clone_issue.ps1</code>) - Cross-platform PowerShell solution</li>
  <li><strong>Shell Script</strong> (<code class="language-plaintext highlighter-rouge">clone_issue.sh</code>) - Traditional bash script</li>
</ol>

<p>Both scripts:</p>
<ul>
  <li>Accept issue number as a parameter</li>
  <li>Retrieve the original issue’s title and body</li>
  <li>Create a new issue with identical content</li>
  <li>Add a reference note (“Cloned from #[original_issue_number]”)</li>
  <li>Automatically close the original issue with a comment referencing the new issue</li>
</ul>

<p><strong>clone_issue.ps1</strong></p>

<div class="language-ps highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nf">#!</span><span class="nv">/usr/bin/env</span> <span class="nf">pwsh</span>

<span class="p">&lt;</span><span class="nf">#</span>
<span class="nf">.SYNOPSIS</span>
    <span class="nf">Clone</span> <span class="nf">a</span> <span class="nf">GitHub</span> <span class="nf">issue</span> <span class="nf">by</span> <span class="nf">creating</span> <span class="nf">a</span> <span class="nf">new</span> <span class="nf">issue</span> <span class="nf">with</span> <span class="nf">the</span> <span class="nf">same</span> <span class="nf">title</span> <span class="kr">and</span> <span class="nf">body.</span>

<span class="nf">.AUTHOR</span>
    <span class="nf">Mahedee</span> <span class="nf">Hasan</span>

<span class="nf">.DESCRIPTION</span>
    <span class="nf">This</span> <span class="nf">script</span> <span class="nf">uses</span> <span class="nf">GitHub</span> <span class="nf">CLI</span> <span class="nf">to</span> <span class="nf">retrieve</span> <span class="nf">an</span> <span class="nf">existing</span> <span class="nf">issue</span> <span class="kr">and</span> <span class="nf">create</span> <span class="nf">a</span> <span class="nf">new</span> <span class="nf">one</span>
    <span class="nf">with</span> <span class="nf">the</span> <span class="nf">same</span> <span class="nf">content,</span> <span class="nf">adding</span> <span class="nf">a</span> <span class="nf">note</span> <span class="nf">that</span> <span class="nf">it</span> <span class="nf">was</span> <span class="nf">cloned</span> <span class="nf">from</span> <span class="nf">the</span> <span class="nf">original.</span>
    <span class="nf">After</span> <span class="nf">successful</span> <span class="nf">creation,</span> <span class="nf">it</span> <span class="nf">closes</span> <span class="nf">the</span> <span class="nf">original</span> <span class="nf">issue</span> <span class="nf">with</span> <span class="nf">a</span> <span class="nf">comment</span> <span class="nf">referencing</span> <span class="nf">the</span> <span class="nf">new</span> <span class="nf">issue.</span>

<span class="nf">.PARAMETER</span> <span class="nf">IssueNumber</span>
    <span class="nf">The</span> <span class="nf">issue</span> <span class="nf">number</span> <span class="nf">to</span> <span class="nf">clone.</span>

<span class="nf">.PARAMETER</span> <span class="nf">Repository</span>
    <span class="nf">The</span> <span class="nf">repository</span> <span class="nf">in</span> <span class="nf">format</span> <span class="nf">"owner</span><span class="nv">/repo".</span> <span class="nf">Defaults</span> <span class="nf">to</span> <span class="nf">"mahedee</span><span class="nv">/rnd".</span>

<span class="nf">.EXAMPLE</span>
    <span class="nf">.\clone_issue.ps1</span> <span class="nf">-IssueNumber</span> <span class="mf">7</span>
    
<span class="nf">.EXAMPLE</span>
    <span class="nf">.\clone_issue.ps1</span> <span class="nf">-IssueNumber</span> <span class="mf">7</span> <span class="nf">-Repository</span> <span class="nf">"username</span><span class="nv">/repository"</span>

<span class="nf">.EXAMPLE</span>
    <span class="nf">.\clone_issue.ps1</span> <span class="mf">9</span>
<span class="nf">#</span><span class="p">&gt;</span>

<span class="nf">param</span><span class="s">(
    [Parameter(Mandatory=$true)]
    [int]$IssueNumber,
    
    [Parameter(Mandatory=$false)]
    [string]$Repository = "arisha/myrepo"
)</span>

<span class="nf">#</span> <span class="nf">Check</span> <span class="kr">if</span> <span class="nf">GitHub</span> <span class="nf">CLI</span> <span class="nf">is</span> <span class="nf">available</span>
<span class="nf">try</span> <span class="p">{</span>
    <span class="nf">$null</span> <span class="nf">=</span> <span class="nf">Get-Command</span> <span class="nf">gh</span> <span class="nf">-ErrorAction</span> <span class="nf">Stop</span>
<span class="p">}</span> <span class="nf">catch</span> <span class="p">{</span>
    <span class="nf">Write-Error</span> <span class="nf">"GitHub</span> <span class="nf">CLI</span> <span class="s">(gh)</span> <span class="nf">is</span> <span class="kr">not</span> <span class="nf">found.</span> <span class="nf">Please</span> <span class="nf">install</span> <span class="nf">GitHub</span> <span class="nf">CLI</span> <span class="kr">and</span> <span class="nf">ensure</span> <span class="nf">it's</span> <span class="nf">in</span> <span class="nf">your</span> <span class="nf">PATH."</span>
    <span class="nb">exit</span> <span class="mf">1</span>
<span class="p">}</span>

<span class="nf">try</span> <span class="p">{</span>
    <span class="nf">Write-Host</span> <span class="nf">"Retrieving</span> <span class="nf">issue</span> <span class="nf">#$IssueNumber</span> <span class="nf">from</span> <span class="nf">repository</span> <span class="nf">$Repository..."</span>
    
    <span class="nf">#</span> <span class="nf">Get</span> <span class="nf">issue</span> <span class="nf">title</span>
    <span class="nf">$title</span> <span class="nf">=</span> <span class="nf">gh</span> <span class="nf">issue</span> <span class="nf">view</span> <span class="nf">$IssueNumber</span> <span class="nf">-R</span> <span class="nf">$Repository</span> <span class="nf">--json</span> <span class="nf">title</span> <span class="nf">-q</span> <span class="nf">.title</span>
    <span class="kr">if</span> <span class="s">($LASTEXITCODE -ne 0)</span> <span class="p">{</span>
        <span class="nf">Write-Error</span> <span class="nf">"Failed</span> <span class="nf">to</span> <span class="nf">retrieve</span> <span class="nf">issue</span> <span class="nf">#$IssueNumber.</span> <span class="nf">Please</span> <span class="nf">check</span> <span class="kr">if</span> <span class="nf">the</span> <span class="nf">issue</span> <span class="nf">exists</span> <span class="kr">and</span> <span class="nf">you</span> <span class="nf">have</span> <span class="nf">access</span> <span class="nf">to</span> <span class="nf">the</span> <span class="nf">repository."</span>
        <span class="nb">exit</span> <span class="mf">1</span>
    <span class="p">}</span>
    
    <span class="nf">#</span> <span class="nf">Get</span> <span class="nf">issue</span> <span class="nf">body</span>
    <span class="nf">$body</span> <span class="nf">=</span> <span class="nf">gh</span> <span class="nf">issue</span> <span class="nf">view</span> <span class="nf">$IssueNumber</span> <span class="nf">-R</span> <span class="nf">$Repository</span> <span class="nf">--json</span> <span class="nf">body</span> <span class="nf">-q</span> <span class="nf">.body</span>
    <span class="kr">if</span> <span class="s">($LASTEXITCODE -ne 0)</span> <span class="p">{</span>
        <span class="nf">Write-Error</span> <span class="nf">"Failed</span> <span class="nf">to</span> <span class="nf">retrieve</span> <span class="nf">issue</span> <span class="nf">body</span> <span class="kr">for</span> <span class="nf">#$IssueNumber."</span>
        <span class="nb">exit</span> <span class="mf">1</span>
    <span class="p">}</span>
    
    <span class="nf">#</span> <span class="nf">Create</span> <span class="nf">the</span> <span class="nf">cloned</span> <span class="nf">issue</span> <span class="nf">body</span> <span class="nf">with</span> <span class="nf">reference</span> <span class="nf">to</span> <span class="nf">original</span>
    <span class="nf">$clonedBody</span> <span class="nf">=</span> <span class="nf">"$body`n`nCloned</span> <span class="nf">from</span> <span class="nf">#$IssueNumber"</span>
    
    <span class="nf">Write-Host</span> <span class="nf">"Creating</span> <span class="nf">new</span> <span class="nf">issue</span> <span class="nf">with</span> <span class="nf">title:</span> <span class="nf">$title"</span>
    
    <span class="nf">#</span> <span class="nf">Create</span> <span class="nf">new</span> <span class="nf">issue</span> <span class="kr">and</span> <span class="nf">capture</span> <span class="nf">the</span> <span class="nf">result</span>
    <span class="nf">$createOutput</span> <span class="nf">=</span> <span class="nf">gh</span> <span class="nf">issue</span> <span class="nf">create</span> <span class="nf">-R</span> <span class="nf">$Repository</span> <span class="nf">--title</span> <span class="nf">"$title"</span> <span class="nf">--body</span> <span class="nf">"$clonedBody"</span> <span class="mf">2</span><span class="p">&gt;</span><span class="nf">&amp;1</span>
    
    <span class="kr">if</span> <span class="s">($LASTEXITCODE -eq 0)</span> <span class="p">{</span>
        <span class="nf">#</span> <span class="nf">Extract</span> <span class="nf">issue</span> <span class="nf">number</span> <span class="nf">from</span> <span class="nf">URL</span> <span class="s">(format: https://github.com/owner/repo/issues/123)</span>
        <span class="kr">if</span> <span class="s">($createOutput -match '/issues/(</span><span class="se">\</span><span class="err">d+</span><span class="se">)</span><span class="s">$') {
            $newIssueNumber = $matches[1]
            Write-Host "Successfully created new issue #$newIssueNumber!" -ForegroundColor Green
            
            # Close the original issue with a comment
            Write-Host "Closing original issue #$IssueNumber..."
            $closeComment = "Created new issue #$newIssueNumber"
            
            gh issue close $IssueNumber -R $Repository --comment "$closeComment"
            
            if ($LASTEXITCODE -eq 0) {
                Write-Host "Successfully closed original issue #$IssueNumber with reference to new issue #$newIssueNumber!" -ForegroundColor Green
            } else {
                Write-Warning "New issue #$newIssueNumber was created, but failed to close the original issue #$IssueNumber."
            }
        } else {
            Write-Host "New issue created successfully, but couldn't extract issue number from output: $createOutput" -ForegroundColor Yellow
            Write-Warning "Please manually close issue #$IssueNumber if needed."
        }
    } else {
        Write-Error "Failed to create the cloned issue. Error: $createOutput"
        exit 1
    }
    
} catch {
    Write-Error "An error occurred: $($_.Exception.Message)"
    exit 1
}
</span></code></pre></div></div>

<p><strong>clone_issue.sh</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="c"># Clone a GitHub issue by creating a new issue with the same title and body,</span>
<span class="c"># then close the original issue with a comment referencing the new one.</span>
<span class="c">#</span>
<span class="c"># Author: Mahedee Hasan</span>
<span class="c">#</span>
<span class="c"># Usage: ./clone_issue.sh &lt;issue_number&gt; [repository]</span>
<span class="c">#</span>
<span class="c"># Parameters:</span>
<span class="c">#   issue_number: The issue number to clone (required)</span>
<span class="c">#   repository:   The repository in format "owner/repo" (optional, defaults to "mahedee/rnd")</span>
<span class="c">#</span>
<span class="c"># Examples:</span>
<span class="c">#   ./clone_issue.sh 7</span>
<span class="c">#   ./clone_issue.sh 7 "username/repository"</span>

<span class="c"># Add GitHub CLI to PATH</span>
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PATH</span><span class="s2">:/c/Program Files/GitHub CLI"</span>

<span class="c"># Check if issue number is provided</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Error: Issue number is required"</span>
    <span class="nb">echo</span> <span class="s2">"Usage: </span><span class="nv">$0</span><span class="s2"> &lt;issue_number&gt; [repository]"</span>
    <span class="nb">exit </span>1
<span class="k">fi

</span><span class="nv">ISSUE</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">REPO</span><span class="o">=</span><span class="k">${</span><span class="nv">2</span><span class="k">:-</span><span class="s2">"arisha/myrepo"</span><span class="k">}</span>

<span class="c"># Check if GitHub CLI is available</span>
<span class="k">if</span> <span class="o">!</span> <span class="nb">command</span> <span class="nt">-v</span> gh &amp;&gt; /dev/null<span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Error: GitHub CLI (gh) is not found. Please install GitHub CLI and ensure it's in your PATH."</span>
    <span class="nb">exit </span>1
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"Retrieving issue #</span><span class="nv">$ISSUE</span><span class="s2"> from repository </span><span class="nv">$REPO</span><span class="s2">..."</span>

<span class="c"># Get issue title</span>
<span class="nv">TITLE</span><span class="o">=</span><span class="si">$(</span>gh issue view <span class="nv">$ISSUE</span> <span class="nt">-R</span> <span class="nv">$REPO</span> <span class="nt">--json</span> title <span class="nt">-q</span> .title<span class="si">)</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="nt">-ne</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Error: Failed to retrieve issue #</span><span class="nv">$ISSUE</span><span class="s2">. Please check if the issue exists and you have access to the repository."</span>
    <span class="nb">exit </span>1
<span class="k">fi</span>

<span class="c"># Get issue body</span>
<span class="nv">BODY</span><span class="o">=</span><span class="si">$(</span>gh issue view <span class="nv">$ISSUE</span> <span class="nt">-R</span> <span class="nv">$REPO</span> <span class="nt">--json</span> body <span class="nt">-q</span> .body<span class="si">)</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="nt">-ne</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Error: Failed to retrieve issue body for #</span><span class="nv">$ISSUE</span><span class="s2">."</span>
    <span class="nb">exit </span>1
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"Creating new issue with title: </span><span class="nv">$TITLE</span><span class="s2">"</span>

<span class="c"># Create the cloned issue body with reference to original</span>
<span class="nv">CLONED_BODY</span><span class="o">=</span><span class="s2">"</span><span class="nv">$BODY</span><span class="s2">

Cloned from #</span><span class="nv">$ISSUE</span><span class="s2">"</span>

<span class="c"># Create new issue and capture the result</span>
<span class="nv">CREATE_OUTPUT</span><span class="o">=</span><span class="si">$(</span>gh issue create <span class="nt">-R</span> <span class="nv">$REPO</span> <span class="nt">--title</span> <span class="s2">"</span><span class="nv">$TITLE</span><span class="s2">"</span> <span class="nt">--body</span> <span class="s2">"</span><span class="nv">$CLONED_BODY</span><span class="s2">"</span> 2&gt;&amp;1<span class="si">)</span>
<span class="nv">CREATE_EXIT_CODE</span><span class="o">=</span><span class="nv">$?</span>

<span class="k">if</span> <span class="o">[</span> <span class="nv">$CREATE_EXIT_CODE</span> <span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    <span class="c"># Extract issue number from URL (format: https://github.com/owner/repo/issues/123)</span>
    <span class="k">if</span> <span class="o">[[</span> <span class="nv">$CREATE_OUTPUT</span> <span class="o">=</span>~ /issues/<span class="o">([</span>0-9]+<span class="o">)</span><span class="nv">$ </span><span class="o">]]</span><span class="p">;</span> <span class="k">then
        </span><span class="nv">NEW_ISSUE_NUMBER</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">BASH_REMATCH</span><span class="p">[1]</span><span class="k">}</span><span class="s2">"</span>
        <span class="nb">echo</span> <span class="s2">"Successfully created new issue #</span><span class="nv">$NEW_ISSUE_NUMBER</span><span class="s2">!"</span>
        
        <span class="c"># Close the original issue with a comment</span>
        <span class="nb">echo</span> <span class="s2">"Closing original issue #</span><span class="nv">$ISSUE</span><span class="s2">..."</span>
        <span class="nv">CLOSE_COMMENT</span><span class="o">=</span><span class="s2">"Created new issue #</span><span class="nv">$NEW_ISSUE_NUMBER</span><span class="s2">"</span>
        
        gh issue close <span class="nv">$ISSUE</span> <span class="nt">-R</span> <span class="nv">$REPO</span> <span class="nt">--comment</span> <span class="s2">"</span><span class="nv">$CLOSE_COMMENT</span><span class="s2">"</span>
        
        <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
            </span><span class="nb">echo</span> <span class="s2">"Successfully closed original issue #</span><span class="nv">$ISSUE</span><span class="s2"> with reference to new issue #</span><span class="nv">$NEW_ISSUE_NUMBER</span><span class="s2">!"</span>
        <span class="k">else
            </span><span class="nb">echo</span> <span class="s2">"Warning: New issue #</span><span class="nv">$NEW_ISSUE_NUMBER</span><span class="s2"> was created, but failed to close the original issue #</span><span class="nv">$ISSUE</span><span class="s2">."</span>
        <span class="k">fi
    else
        </span><span class="nb">echo</span> <span class="s2">"New issue created successfully, but couldn't extract issue number from output: </span><span class="nv">$CREATE_OUTPUT</span><span class="s2">"</span>
        <span class="nb">echo</span> <span class="s2">"Warning: Please manually close issue #</span><span class="nv">$ISSUE</span><span class="s2"> if needed."</span>
    <span class="k">fi
else
    </span><span class="nb">echo</span> <span class="s2">"Error: Failed to create the cloned issue. Error: </span><span class="nv">$CREATE_OUTPUT</span><span class="s2">"</span>
    <span class="nb">exit </span>1
<span class="k">fi</span>
</code></pre></div></div>

<h2 id="powershell-script-implementation">PowerShell Script Implementation</h2>

<h3 id="script-features">Script Features</h3>

<p>The PowerShell script (<code class="language-plaintext highlighter-rouge">clone_issue.ps1</code>) offers:</p>
<ul>
  <li><strong>Parameter validation</strong> with mandatory issue number</li>
  <li><strong>Flexible repository specification</strong> (defaults to “arisha/myrepo”)</li>
  <li><strong>Comprehensive error handling</strong> with detailed messages</li>
  <li><strong>Built-in help documentation</strong> using PowerShell comment-based help</li>
  <li><strong>Cross-platform compatibility</strong> (Windows, Linux, macOS)</li>
</ul>

<h3 id="usage-examples">Usage Examples</h3>

<p><strong>Basic usage:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\clone_issue.ps1</span><span class="w"> </span><span class="nt">-IssueNumber</span><span class="w"> </span><span class="nx">9</span><span class="w">
</span></code></pre></div></div>

<p><strong>Short parameter syntax:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\clone_issue.ps1</span><span class="w"> </span><span class="nx">9</span><span class="w">
</span></code></pre></div></div>

<p><strong>With custom repository:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\clone_issue.ps1</span><span class="w"> </span><span class="nt">-IssueNumber</span><span class="w"> </span><span class="nx">9</span><span class="w"> </span><span class="nt">-Repository</span><span class="w"> </span><span class="s2">"username/repository"</span><span class="w">
</span></code></pre></div></div>

<p><strong>Get help:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-Help</span><span class="w"> </span><span class="o">.</span><span class="nx">\clone_issue.ps1</span><span class="w"> </span><span class="nt">-Full</span><span class="w">
</span></code></pre></div></div>

<h3 id="script-workflow">Script Workflow</h3>

<ol>
  <li><strong>Validation</strong>: Checks if GitHub CLI is installed and accessible</li>
  <li><strong>Retrieval</strong>: Fetches the original issue’s title and body using <code class="language-plaintext highlighter-rouge">gh issue view</code></li>
  <li><strong>Creation</strong>: Creates a new issue with the same content plus a clone reference</li>
  <li><strong>Extraction</strong>: Parses the new issue URL to extract the issue number</li>
  <li><strong>Closure</strong>: Closes the original issue with a comment linking to the new issue</li>
</ol>

<h2 id="shell-script-implementation">Shell Script Implementation</h2>

<h3 id="script-features-1">Script Features</h3>

<p>The shell script (<code class="language-plaintext highlighter-rouge">clone_issue.sh</code>) provides:</p>
<ul>
  <li><strong>Command-line parameter support</strong> for issue number and repository</li>
  <li><strong>POSIX-compliant bash scripting</strong> for maximum compatibility</li>
  <li><strong>Regex pattern matching</strong> for URL parsing</li>
  <li><strong>Comprehensive error checking</strong> with exit codes</li>
  <li><strong>Git Bash compatibility</strong> on Windows</li>
</ul>

<h3 id="usage-examples-1">Usage Examples</h3>

<p><strong>Basic usage:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./clone_issue.sh 9
</code></pre></div></div>

<p><strong>With custom repository:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./clone_issue.sh 9 <span class="s2">"username/repository"</span>
</code></pre></div></div>

<p><strong>Make script executable (Linux/macOS):</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod</span> +x clone_issue.sh
./clone_issue.sh 9
</code></pre></div></div>

<h3 id="script-workflow-1">Script Workflow</h3>

<ol>
  <li><strong>Parameter Check</strong>: Validates that issue number is provided</li>
  <li><strong>CLI Verification</strong>: Ensures GitHub CLI is available in PATH</li>
  <li><strong>Data Retrieval</strong>: Uses <code class="language-plaintext highlighter-rouge">gh issue view</code> with JSON output parsing</li>
  <li><strong>Issue Creation</strong>: Creates new issue and captures the output</li>
  <li><strong>URL Parsing</strong>: Extracts issue number using bash regex matching</li>
  <li><strong>Original Closure</strong>: Closes the original issue with reference comment</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>This comprehensive solution provides robust GitHub issue cloning capabilities using both PowerShell and shell scripts. The automated workflow saves time while maintaining data integrity and providing proper cross-references between original and cloned issues.</p>

<p>Choose the PowerShell script for cross-platform compatibility and advanced parameter handling, or use the shell script for lightweight, POSIX-compliant environments. Both scripts provide the same core functionality with appropriate error handling and user feedback. Happy coding!</p>]]></content><author><name>Mahedee Hasan</name></author><category term="Github" /><category term="DevOps" /><category term="Tools" /><category term="Command" /><category term="github" /><category term="git" /><category term="powershell" /><category term="shell" /><category term="bash" /><category term="automation" /><category term="github-issues" /><category term="devops" /><category term="command" /><category term="tools" /><summary type="html"><![CDATA[Step-by-step guide to cloning GitHub issues using PowerShell and shell scripts to automate issue duplication and improve workflow efficiency]]></summary></entry></feed>