<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dotnet</title>
    <description>The latest articles tagged 'dotnet' on DEV Community.</description>
    <link>https://dev.to/t/dotnet</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/dotnet"/>
    <language>en</language>
    <item>
      <title>.NET System Design ও Application Architecture: A2Z সম্পূর্ণ গাইড</title>
      <dc:creator>Rakib Ahasan</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:37:24 +0000</pubDate>
      <link>https://dev.to/rakibahasan/net-system-design-o-application-architecture-a2z-smpuurnn-gaaidd-een</link>
      <guid>https://dev.to/rakibahasan/net-system-design-o-application-architecture-a2z-smpuurnn-gaaidd-een</guid>
      <description>&lt;p&gt;Oct 28, 2026 · &lt;a class="mentioned-user" href="https://dev.to/rakib"&gt;@rakib&lt;/a&gt; Ahasan&lt;/p&gt;

&lt;h2&gt;
  
  
  ভূমিকা: এই গাইড কেন, কার জন্য, কীভাবে পড়বেন
&lt;/h2&gt;

&lt;p&gt;System design শেখার সবচেয়ে বড় সমস্যা হলো তথ্য ছড়ানো: একটা ব্লগে CAP theorem, আরেকটায় Microservices, তৃতীয়টায় Redis। কোনোটাই বলে না কখন কোনটা লাগে। এই গাইডে সব একসাথে, একটা ধারাবাহিক গল্পের মধ্য দিয়ে, .NET (C# ও ASP.NET Core) এর উদাহরণসহ।&lt;/p&gt;

&lt;h3&gt;
  
  
  এই গাইড কাদের জন্য
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;যারা C# জানেন, কিন্তু "architecture" শব্দটা শুনলে ঘাবড়ে যান (Beginner)&lt;/li&gt;
&lt;li&gt;যারা ২-৩ বছর কাজ করছেন, কিন্তু কেন Clean Architecture বা CQRS লাগে বুঝতে পারেন না (Intermediate)&lt;/li&gt;
&lt;li&gt;যারা Senior/Lead হতে চান এবং system design interview বা real project এ trade-off নিয়ে সিদ্ধান্ত নিতে চান (Advanced)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  কীভাবে পড়বেন
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;আপনি কোথায়&lt;/th&gt;
&lt;th&gt;কী করবেন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;একদম নতুন&lt;/td&gt;
&lt;td&gt;Part 1 থেকে ক্রমানুসারে পড়ুন, কোড হাতে লিখে চালান&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;কিছুটা অভিজ্ঞ&lt;/td&gt;
&lt;td&gt;Part 2, 3 ও 4 মন দিয়ে পড়ুন, বাকিগুলো স্কিম করুন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interview প্রস্তুতি&lt;/td&gt;
&lt;td&gt;Part 1, 6, 7, 11, 12 আগে পড়ুন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real project এ আটকে আছেন&lt;/td&gt;
&lt;td&gt;সূচি দেখে সরাসরি সংশ্লিষ্ট Part এ যান&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  গল্পের চরিত্র: ShopEasy
&lt;/h3&gt;

&lt;p&gt;পুরো গাইডে আমরা একটা কাল্পনিক ই-কমার্স অ্যাপ &lt;strong&gt;ShopEasy&lt;/strong&gt; ধরে এগোব। রাফি নামের একজন ডেভেলপার ঢাকায় একা এটা শুরু করে।&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;দিন ১:&lt;/strong&gt; ১০০ জন ইউজার, একটা ASP.NET Core অ্যাপ, একটা SQL Server। সব ঠিকঠাক।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;৬ মাস পর:&lt;/strong&gt; ১০,০০০ ইউজার। পেজ ধীর, কোড এলোমেলো, নতুন ডেভেলপার এসে কিছু বুঝছে না।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;১ বছর পর:&lt;/strong&gt; ঈদের সেলে ১ লক্ষ ইউজার একসাথে ঢুকল, সাইট বসে গেল। পেমেন্ট দুইবার কাটল।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;২ বছর পর:&lt;/strong&gt; ১০ জনের টিম, একসাথে কাজ করলে একে অন্যের কোড ভাঙছে।&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;প্রতিটি সমস্যার সমাধান হিসেবে একটা নতুন concept আসবে। এভাবে আপনি শুধু "কী" নয়, "কেন" ও শিখবেন। এটাই সত্যিকারের architect এর মানসিকতা।&lt;/p&gt;

&lt;h3&gt;
  
  
  System Design আসলে কী?
&lt;/h3&gt;

&lt;p&gt;সহজ কথায়: &lt;strong&gt;একটা সফটওয়্যার কীভাবে অনেক মানুষের জন্য, নির্ভরযোগ্যভাবে, দ্রুত এবং সাশ্রয়ীভাবে কাজ করবে, তার পরিকল্পনা।&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;বাড়ি বানানোর সাথে তুলনা করুন। ঘর (code) ঠিক থাকলেও যদি ভিত্তি (architecture) দুর্বল হয়, তিনতলার পর আর বাড়ানো যায় না। আবার একটা ছোট টিনের ঘরের জন্য আকাশচুম্বী ভবনের ভিত্তি বানানোও অপচয়। ভালো architect জানেন &lt;strong&gt;কখন কতটা লাগবে&lt;/strong&gt;।&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture vs Design: পার্থক্য
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Application Architecture&lt;/th&gt;
&lt;th&gt;System Design&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ফোকাস&lt;/td&gt;
&lt;td&gt;অ্যাপের ভেতরের কাঠামো, layer, module&lt;/td&gt;
&lt;td&gt;পুরো সিস্টেম: server, DB, cache, network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;প্রশ্ন&lt;/td&gt;
&lt;td&gt;কোড কীভাবে সাজাব?&lt;/td&gt;
&lt;td&gt;লক্ষ ইউজার কীভাবে সামলাব?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;উদাহরণ&lt;/td&gt;
&lt;td&gt;Clean Architecture, CQRS&lt;/td&gt;
&lt;td&gt;Load Balancer, Sharding, CDN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;দুটোই একে অপরের সাথে জড়িত। এই গাইডে দুটোই পাবেন।&lt;/p&gt;

&lt;h3&gt;
  
  
  সবচেয়ে গুরুত্বপূর্ণ কথা: Trade-off
&lt;/h3&gt;

&lt;p&gt;System design এ "সেরা সমাধান" বলে কিছু নেই। আছে &lt;strong&gt;কোন পরিস্থিতিতে কোনটা উপযুক্ত&lt;/strong&gt;। প্রতিটি সিদ্ধান্তে কিছু পাওয়া যায়, কিছু ছাড়তে হয়। এই গাইডের প্রতিটি অধ্যায়ে আমরা এই প্রশ্ন করব: &lt;em&gt;এর দাম কী?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  প্রস্তুতি
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;C# এর basics (class, interface, async/await, LINQ)&lt;/li&gt;
&lt;li&gt;ASP.NET Core এর প্রাথমিক ধারণা&lt;/li&gt;
&lt;li&gt;SQL এর basics&lt;/li&gt;
&lt;li&gt;কোড চালাতে .NET 9 বা তার পরের SDK (এই গাইডের কোড .NET 8+ এ চলবে)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: ভিত্তি: নেটওয়ার্ক, Scalability ও Distributed ধারণা
&lt;/h2&gt;

&lt;p&gt;আপনি যদি এই Part ভালোভাবে বোঝেন, বাকি সব Part সহজ লাগবে। এখানে আমরা সেই শব্দগুলো শিখব, যেগুলো system design এর প্রতিটি আলোচনায় আসে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.১ Client-Server মডেল
&lt;/h3&gt;

&lt;p&gt;আপনি ব্রাউজারে &lt;code&gt;shopeasy.com&lt;/code&gt; লিখলে যা ঘটে:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ব্রাউজার (&lt;strong&gt;Client&lt;/strong&gt;) একটা অনুরোধ (&lt;strong&gt;Request&lt;/strong&gt;) পাঠায়।&lt;/li&gt;
&lt;li&gt;কোনো কম্পিউটার (&lt;strong&gt;Server&lt;/strong&gt;) সেটা গ্রহণ করে, প্রসেস করে।&lt;/li&gt;
&lt;li&gt;সার্ভার একটা উত্তর (&lt;strong&gt;Response&lt;/strong&gt;) ফেরত দেয়।&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;রেস্টুরেন্টের উদাহরণ: আপনি (client) ওয়েটারকে অর্ডার দিলেন, রান্নাঘর (server) রান্না করল, ওয়েটার খাবার এনে দিল। ওয়েটার হলো network।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// সবচেয়ে ছোট ASP.NET Core server&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello from ShopEasy!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ১.২ ব্রাউজারে URL লিখলে আসলে কী ঘটে (পুরো যাত্রা)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS lookup:&lt;/strong&gt; &lt;code&gt;shopeasy.com&lt;/code&gt; একটা নাম। কম্পিউটার চেনে IP ঠিকানা (যেমন &lt;code&gt;203.0.113.10&lt;/code&gt;)। DNS হলো ইন্টারনেটের ফোনবুক, নাম দিলে IP বলে দেয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP connection:&lt;/strong&gt; client ও server এর মধ্যে সংযোগ তৈরি হয় (3-way handshake: SYN, SYN-ACK, ACK)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS handshake:&lt;/strong&gt; HTTPS হলে এনক্রিপশন কী বিনিময় হয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP request:&lt;/strong&gt; &lt;code&gt;GET /products HTTP/1.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server processing:&lt;/strong&gt; load balancer, web server, application, database।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP response:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt; সহ JSON/HTML।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser render:&lt;/strong&gt; পেজ দেখানো।&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;প্রতিটি ধাপে সময় লাগে। এগুলো বোঝা মানে বোঝা, ধীর কোথায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.৩ HTTP: সার্ভারের সাথে কথা বলার ভাষা
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Method গুলো:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;কাজ&lt;/th&gt;
&lt;th&gt;Safe?&lt;/th&gt;
&lt;th&gt;Idempotent?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;ডেটা পড়া&lt;/td&gt;
&lt;td&gt;হ্যাঁ&lt;/td&gt;
&lt;td&gt;হ্যাঁ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;নতুন কিছু তৈরি&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;সম্পূর্ণ replace&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;হ্যাঁ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;আংশিক পরিবর্তন&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;সাধারণত না&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;মুছে ফেলা&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;হ্যাঁ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Idempotent&lt;/strong&gt; মানে: একই request একাধিকবার পাঠালে ফলাফল একই থাকে। এটা Part 5 এ অত্যন্ত গুরুত্বপূর্ণ, কারণ নেটওয়ার্ক ফেল করলে client আবার চেষ্টা (retry) করে। ShopEasy তে পেমেন্ট দুবার কাটার মূল কারণ এটাই।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status code এর পরিবার:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;2xx&lt;/code&gt; সফল (200 OK, 201 Created, 204 No Content)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3xx&lt;/code&gt; redirect (301, 302, 304 Not Modified)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;4xx&lt;/code&gt; client এর ভুল (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 429 Too Many Requests)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;5xx&lt;/code&gt; server এর ভুল (500, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১.৪ Latency, Throughput ও Bandwidth
&lt;/h3&gt;

&lt;p&gt;তিনটি শব্দ প্রায়ই গুলিয়ে যায়। পানির পাইপের উদাহরণ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; পানি পাইপের এক মাথা থেকে অন্য মাথায় পৌঁছাতে কত সময় লাগে (যেমন ১২০ ms)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bandwidth:&lt;/strong&gt; পাইপ কত মোটা, অর্থাৎ সর্বোচ্চ কত পানি যেতে পারে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput:&lt;/strong&gt; আসলে প্রতি সেকেন্ডে কত পানি যাচ্ছে (যেমন ৫০০ request/sec)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;প্রতিটি অপারেশনের আনুমানিক সময় জানা থাকলে design সহজ হয়:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;অপারেশন&lt;/th&gt;
&lt;th&gt;আনুমানিক সময়&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;মেমরি থেকে পড়া&lt;/td&gt;
&lt;td&gt;~100 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSD থেকে পড়া&lt;/td&gt;
&lt;td&gt;~100 µs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;একই datacenter এর মধ্যে network call&lt;/td&gt;
&lt;td&gt;~0.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database query (index সহ)&lt;/td&gt;
&lt;td&gt;~1-10 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ঢাকা থেকে সিঙ্গাপুর network round trip&lt;/td&gt;
&lt;td&gt;~60-80 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ঢাকা থেকে ইউরোপ/আমেরিকা&lt;/td&gt;
&lt;td&gt;~150-250 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(সংখ্যাগুলো আনুমানিক; হার্ডওয়্যার ও নেটওয়ার্ক ভেদে বদলায়।) শিক্ষা: &lt;strong&gt;network call ও disk মেমরির চেয়ে বহু গুণ ধীর&lt;/strong&gt;। তাই caching এত শক্তিশালী।&lt;/p&gt;

&lt;p&gt;পারসেন্টাইল: গড় (average) response time প্রায়ই মিথ্যা বলে। &lt;strong&gt;p95/p99 latency&lt;/strong&gt; দেখুন। p99 = ২০০ms মানে ৯৯% request ২০০ms এর মধ্যে শেষ হয়, বাকি ১% এর চেয়ে ধীর। বড় সিস্টেমে ১% ও লক্ষ লক্ষ মানুষ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.৫ Scalability: সিস্টেম বড় হলে সামলানোর ক্ষমতা
&lt;/h3&gt;

&lt;p&gt;ShopEasy এর ঈদের সেলে ট্রাফিক ১০০ গুণ বাড়ল। দুটি উপায়:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertical Scaling (Scale Up):&lt;/strong&gt; একই সার্ভারে বেশি CPU/RAM দেওয়া।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;সুবিধা: সহজ, কোড বদলাতে হয় না।&lt;/li&gt;
&lt;li&gt;অসুবিধা: একটা সীমা আছে, দাম অতিরিক্ত বাড়ে, single point of failure।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Horizontal Scaling (Scale Out):&lt;/strong&gt; আরও অনেক সার্ভার যোগ করা।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;সুবিধা: প্রায় অসীম বাড়ানো যায়, একটা সার্ভার গেলে বাকিরা চলে।&lt;/li&gt;
&lt;li&gt;অসুবিধা: জটিলতা বাড়ে (load balancing, shared state, data consistency)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;উপমা: একজন সুপারম্যান শ্রমিক (vertical) বনাম অনেক সাধারণ শ্রমিক (horizontal)।&lt;/p&gt;

&lt;h4&gt;
  
  
  Stateless বনাম Stateful: Horizontal scaling এর চাবিকাঠি
&lt;/h4&gt;

&lt;p&gt;ধরুন ইউজারের shopping cart সার্ভার-১ এর মেমরিতে আছে। পরের request load balancer সার্ভার-২ তে পাঠাল। cart উধাও!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Stateful: scale করা যায় না&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CartController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_carts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// এই server এর মেমরিতে&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Stateless: state বাইরে (Redis/DB) রাখা হয়েছে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CartController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICartStore&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// যেকোনো server এ একই ফল&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; application server কে stateless রাখুন। state যাবে database, cache বা বাইরের store এ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.৬ Availability ও Reliability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Availability&lt;/strong&gt; = সিস্টেম কত সময় চালু থাকে। "nine" দিয়ে মাপা হয়:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Availability&lt;/th&gt;
&lt;th&gt;বছরে সর্বোচ্চ বন্ধ থাকার সময়&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;99% (দুই নাইন)&lt;/td&gt;
&lt;td&gt;~৩.৬৫ দিন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;99.9% (তিন নাইন)&lt;/td&gt;
&lt;td&gt;~৮.৭৬ ঘণ্টা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;99.99% (চার নাইন)&lt;/td&gt;
&lt;td&gt;~৫২.৬ মিনিট&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;99.999% (পাঁচ নাইন)&lt;/td&gt;
&lt;td&gt;~৫.২৬ মিনিট&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;প্রতিটি অতিরিক্ত nine এর খরচ ও জটিলতা নাটকীয়ভাবে বাড়ে। ব্যবসার প্রয়োজন বুঝে লক্ষ্য ঠিক করুন; একটি ব্লগের জন্য ৯৯.৯৯৯% দরকার নেই।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt; = সিস্টেম ঠিকঠাক কাজ করছে কি না (ভুল ফলাফল দিচ্ছে না তো?)। একটা সিস্টেম available কিন্তু unreliable হতে পারে (সবসময় চালু, কিন্তু ভুল ব্যালেন্স দেখাচ্ছে)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redundancy:&lt;/strong&gt; একই কাজের জন্য একাধিক কপি রাখা, যাতে একটা ভাঙলে অন্যটা কাজ করে। &lt;strong&gt;Single Point of Failure (SPOF):&lt;/strong&gt; যে একটি জিনিস ভাঙলে পুরো সিস্টেম বসে যায়। Architect এর কাজ হলো SPOF খুঁজে বের করা।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SLI, SLO, SLA:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SLI:&lt;/strong&gt; আসলে কী মাপছেন (যেমন সফল request এর শতাংশ)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLO:&lt;/strong&gt; লক্ষ্য (যেমন ৯৯.৯% সফল)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLA:&lt;/strong&gt; গ্রাহকের সাথে চুক্তি, ভাঙলে জরিমানা।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১.৭ Distributed System এর মূল সমস্যা
&lt;/h3&gt;

&lt;p&gt;একটা সার্ভারে সব সহজ। একাধিক সার্ভার হলে নতুন সমস্যা আসে, যেগুলোকে বলে &lt;em&gt;Fallacies of Distributed Computing&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;নেটওয়ার্ক নির্ভরযোগ্য নয় (প্যাকেট হারায়)&lt;/li&gt;
&lt;li&gt;Latency শূন্য নয়&lt;/li&gt;
&lt;li&gt;সার্ভারগুলোর ঘড়ি হুবহু এক নয়&lt;/li&gt;
&lt;li&gt;যেকোনো মুহূর্তে একটা node ফেল করতে পারে&lt;/li&gt;
&lt;li&gt;Partial failure: কিছু অংশ কাজ করে, কিছু করে না। সবচেয়ে কঠিন, কারণ আপনি জানেন না অপর পাশ ফেল করেছে নাকি শুধু ধীর।&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  CAP Theorem
&lt;/h4&gt;

&lt;p&gt;Distributed data store এ তিনটি জিনিসের মধ্যে একসাথে সর্বোচ্চ দুটি পাওয়া যায়:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C (Consistency):&lt;/strong&gt; সবাই সবসময় একই সর্বশেষ ডেটা দেখে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A (Availability):&lt;/strong&gt; প্রতিটি request এর একটা উত্তর মেলে (ভুল/পুরনো হলেও)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P (Partition tolerance):&lt;/strong&gt; নেটওয়ার্ক ভেঙে সার্ভারগুলো আলাদা হয়ে গেলেও সিস্টেম চলে।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;বাস্তবে network partition অনিবার্য, তাই প্রকৃত পছন্দ হলো &lt;strong&gt;CP বনাম AP&lt;/strong&gt;, partition চলাকালীন।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;গল্প:&lt;/strong&gt; ঢাকা ও চট্টগ্রামে ShopEasy এর দুটো ডেটাসেন্টার। দুটোর মাঝের লাইন কেটে গেল। একজন ইউজার ঢাকায় শেষ ১টা পণ্য কিনল, আরেকজন একই সময়ে চট্টগ্রামে।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CP বেছে নিলে:&lt;/strong&gt; চট্টগ্রাম বলবে "এখন অর্ডার নেওয়া যাচ্ছে না"। ডেটা সঠিক থাকল, কিন্তু সেবা বন্ধ।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AP বেছে নিলে:&lt;/strong&gt; দুজনের অর্ডারই নেবে, পরে ঝামেলা মেটাবে (একজনকে ক্ষমা চেয়ে refund)। সেবা চালু, কিন্তু ডেটা সাময়িক অসঙ্গত।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ব্যাংকের ব্যালেন্সে সাধারণত CP, সোশ্যাল মিডিয়ার "like count" এ AP চলে।&lt;/p&gt;

&lt;h4&gt;
  
  
  Consistency মডেল
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strong consistency:&lt;/strong&gt; লেখার পরপর যে কেউ পড়লে নতুন মান পাবে। সহজ, কিন্তু ধীর ও ব্যয়বহুল।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual consistency:&lt;/strong&gt; লেখার পর কিছুক্ষণ কেউ পুরনো মান দেখতে পারে, কিন্তু শেষে সবাই একই মানে পৌঁছায়। দ্রুত, scale করা সহজ।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-your-writes:&lt;/strong&gt; আপনি নিজে যা লিখেছেন সেটা অন্তত নিজে দেখবেন।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;উদাহরণ: Facebook এ কমেন্ট করার পর বন্ধু দুই সেকেন্ড পর দেখে। এটা eventual consistency, আর তাতে কারও ক্ষতি নেই।&lt;/p&gt;

&lt;h4&gt;
  
  
  PACELC
&lt;/h4&gt;

&lt;p&gt;CAP শুধু partition এর সময়ের কথা বলে। PACELC বলে: partition (P) হলে A বা C বেছে নাও; নইলে (Else) স্বাভাবিক সময়েও &lt;strong&gt;Latency (L) বনাম Consistency (C)&lt;/strong&gt; এর মধ্যে বেছে নিতে হয়। শক্তিশালী consistency চাইলে একাধিক node এর সম্মতি লাগে, তাই ধীর হয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.৮ Load Balancer: ট্রাফিক ভাগ করার ট্রাফিক পুলিশ
&lt;/h3&gt;

&lt;p&gt;একাধিক server থাকলে ইউজারের request কোনটায় যাবে? Load Balancer ঠিক করে।&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Round Robin:&lt;/strong&gt; পালা করে (১, ২, ৩, ১, ২, ৩...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least Connections:&lt;/strong&gt; যার কাজ কম, তাকে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP Hash:&lt;/strong&gt; একই ইউজার সবসময় একই server এ (sticky session)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weighted:&lt;/strong&gt; শক্তিশালী server কে বেশি&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Layer 4 বনাম Layer 7:&lt;/strong&gt; L4 শুধু IP/port দেখে (দ্রুত)। L7 HTTP এর ভেতরে (URL, header) দেখে সিদ্ধান্ত নেয়, যেমন &lt;code&gt;/api/*&lt;/code&gt; এক জায়গায়, &lt;code&gt;/images/*&lt;/code&gt; অন্য জায়গায়। Nginx, HAProxy, Azure Application Gateway, AWS ALB জনপ্রিয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Health check:&lt;/strong&gt; load balancer নিয়মিত জিজ্ঞেস করে "তুমি ঠিক আছো?" (&lt;code&gt;/health&lt;/code&gt; endpoint)। ফেল করলে সেই server কে ট্রাফিক দেওয়া বন্ধ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১.৯ Proxy, Reverse Proxy, CDN
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forward proxy:&lt;/strong&gt; ক্লায়েন্টের পক্ষ হয়ে বাইরে যায় (যেমন অফিসের ইন্টারনেট ফিল্টার)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse proxy:&lt;/strong&gt; সার্ভারের সামনে বসে, ক্লায়েন্টের request নিয়ে ভেতরের server এ পাঠায়। কাজ: load balancing, TLS termination, caching, নিরাপত্তা। (Nginx, YARP, Cloudflare)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN (Content Delivery Network):&lt;/strong&gt; ছবি, CSS, JS, ভিডিওর মতো static ফাইলের কপি বিশ্বজুড়ে edge সার্ভারে রাখে। ঢাকার ইউজার সিঙ্গাপুরের edge থেকে ছবি পায়, আমেরিকার origin server থেকে নয়। ফলে latency অনেক কমে।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১.১০ DNS এর আরেকটু গভীরে
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A record:&lt;/strong&gt; নাম থেকে IPv4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CNAME:&lt;/strong&gt; এক নামের alias আরেক নাম&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL:&lt;/strong&gt; cache এ কতক্ষণ রাখা যাবে (কম TTL = দ্রুত বদল, বেশি DNS query)&lt;/li&gt;
&lt;li&gt;DNS দিয়েও load balancing ও geo-routing করা যায় (ইউজারকে কাছের region এ পাঠানো)।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 1 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Network ও disk ধীর; বেশি call মানে বেশি latency।&lt;/li&gt;
&lt;li&gt;Horizontal scaling এর শর্ত: app server stateless।&lt;/li&gt;
&lt;li&gt;Availability এর প্রতিটি nine এর দাম আছে; SPOF খুঁজুন।&lt;/li&gt;
&lt;li&gt;Distributed system এ failure স্বাভাবিক; CAP/PACELC তে trade-off বুঝে বেছে নিন।&lt;/li&gt;
&lt;li&gt;Load balancer, reverse proxy, CDN হলো ট্রাফিক সামলানোর প্রথম অস্ত্র।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 2: Code-level Design: SOLID, Principles ও Design Pattern
&lt;/h2&gt;

&lt;p&gt;বড় architecture এর আগে ছোট কোড ঠিক করতে হয়। ৬ মাস পর ShopEasy এর কোড এমন হয়েছে: একটা &lt;code&gt;OrderService&lt;/code&gt; ক্লাসে ২,০০০ লাইন, যেখানে অর্ডার, পেমেন্ট, ইমেইল, ইনভেন্টরি সব মিশে আছে। একটা জায়গা বদলালে তিন জায়গা ভাঙে। এই Part সেই সমস্যার সমাধান।&lt;/p&gt;

&lt;h3&gt;
  
  
  ২.১ খারাপ কোডের উদাহরণ (সমস্যা চিনুন)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. validation&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Empty"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. database&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server=...;Password=123"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// ... SQL insert ...&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. payment&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"bkash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* bkash API call */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* card API call */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. email&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;smtp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SmtpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"smtp.gmail.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;smtp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;সমস্যা: একাধিক দায়িত্ব, hard-coded dependency, নতুন পেমেন্ট (Nagad) এলে এই ক্লাসই বদলাতে হবে, test লেখা প্রায় অসম্ভব (সত্যিকারের DB ও SMTP লাগে)। SOLID এগুলোই ঠিক করে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ২.২ SOLID: পাঁচটি মূলনীতি
&lt;/h3&gt;

&lt;h4&gt;
  
  
  S: Single Responsibility Principle (SRP)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;একটা ক্লাসের পরিবর্তনের কারণ একটাই হওয়া উচিত।&lt;/strong&gt; একটা ক্লাস একটা কাজ করবে।&lt;/p&gt;

&lt;p&gt;উপমা: একজন শেফ যদি রান্না, হিসাব, ডেলিভারি সব করে, কোনোটাই ভালো হয় না।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SaveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailNotifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  O: Open/Closed Principle (OCP)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;বাড়ানোর জন্য খোলা, বদলানোর জন্য বন্ধ।&lt;/strong&gt; নতুন ফিচার যোগ করতে পুরনো কোড না ছুঁয়ে নতুন কোড লিখুন।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ নতুন পেমেন্ট এলেই if-else বাড়ে&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"bkash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Interface দিয়ে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentMethod&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;PayAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BkashPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardPayment&lt;/span&gt;  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NagadPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* নতুন ফাইল, পুরনো কোড অক্ষত */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  L: Liskov Substitution Principle (LSP)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Parent এর জায়গায় Child বসালে প্রোগ্রাম ঠিকমতো চলতে হবে।&lt;/strong&gt; Child যেন parent এর প্রতিশ্রুতি না ভাঙে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Ostrich bird এর Fly() এ exception ছুড়লে LSP ভাঙে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ostrich&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bird&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotSupportedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ আলাদা interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IFlyable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sparrow&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFlyable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ostrich&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Fly নেই */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;লক্ষণ: কোডে বারবার &lt;code&gt;if (x is SpecificType)&lt;/code&gt; বা &lt;code&gt;NotImplementedException&lt;/code&gt; দেখলে LSP সম্ভবত ভাঙছে।&lt;/p&gt;

&lt;h4&gt;
  
  
  I: Interface Segregation Principle (ISP)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;বড় interface ভেঙে ছোট ছোট interface বানান।&lt;/strong&gt; ক্লায়েন্টকে এমন method মানতে বাধ্য করবেন না যা সে ব্যবহার করে না।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ মোটা interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IWorker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ ছোট interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IWorkable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IFeedable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  D: Dependency Inversion Principle (DIP)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;উচ্চস্তরের মডিউল নিম্নস্তরের concrete ক্লাসের ওপর নির্ভর করবে না; উভয়ই abstraction এর ওপর নির্ভর করবে।&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;উপমা: আপনার ল্যাপটপ চার্জার সরাসরি দেয়ালে ঝালাই করা নেই; মাঝে সকেট (abstraction) আছে, তাই যেকোনো চার্জার লাগে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IPaymentMethod&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;INotifier&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// সবই abstraction&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PayAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;এখন unit test এ fake/mock পাস করা যায়, ও production এ আসল implementation।&lt;/p&gt;

&lt;h3&gt;
  
  
  ২.৩ Dependency Injection (DI) ও IoC
&lt;/h3&gt;

&lt;p&gt;DIP হলো নীতি, &lt;strong&gt;DI&lt;/strong&gt; হলো সেটা বাস্তবায়নের কৌশল: dependency নিজে &lt;code&gt;new&lt;/code&gt; না করে বাইরে থেকে ঢুকিয়ে দেওয়া। .NET এ DI built-in।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SqlOrderRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BkashPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;INotifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmailNotifier&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lifetime (খুব গুরুত্বপূর্ণ):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lifetime&lt;/th&gt;
&lt;th&gt;মানে&lt;/th&gt;
&lt;th&gt;কখন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;প্রতিবার নতুন instance&lt;/td&gt;
&lt;td&gt;হালকা, stateless সার্ভিস&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoped&lt;/td&gt;
&lt;td&gt;প্রতি HTTP request এ একটা&lt;/td&gt;
&lt;td&gt;DbContext, Unit of Work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singleton&lt;/td&gt;
&lt;td&gt;পুরো অ্যাপে একটা&lt;/td&gt;
&lt;td&gt;Cache, config, HttpClient factory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;সাধারণ ভুল (Captive Dependency):&lt;/strong&gt; Singleton এর ভেতরে Scoped service ঢোকানো। Scoped instance আর কখনো ছাড়া পায় না, ফলে DbContext এর পুরনো state ও thread-safety সমস্যা হয়। .NET development mode এ এটা ধরে ফেলে।&lt;/p&gt;

&lt;p&gt;একই interface এর একাধিক implementation লাগলে .NET 8+ এ &lt;strong&gt;Keyed Services&lt;/strong&gt; আছে:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddKeyedScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BkashPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"bkash"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddKeyedScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CardPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/pay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;FromKeyedServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bkash"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IPaymentMethod&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PayAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ২.৪ অন্যান্য মৌলিক নীতি
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DRY (Don't Repeat Yourself):&lt;/strong&gt; একই লজিক ৩ জায়গায় কপি করবেন না। তবে সতর্ক: দুটি কোড দেখতে একই, কিন্তু ভিন্ন কারণে বদলায়, সেগুলো জোর করে এক করলে ক্ষতি হয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KISS (Keep It Simple):&lt;/strong&gt; সহজ সমাধান আগে। জটিল design এর দরকার প্রমাণিত না হওয়া পর্যন্ত নয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAGNI (You Aren't Gonna Need It):&lt;/strong&gt; "হয়তো ভবিষ্যতে লাগবে" ভেবে ফিচার বানাবেন না।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; UI, business logic, data access আলাদা রাখুন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Law of Demeter:&lt;/strong&gt; &lt;code&gt;a.GetB().GetC().DoSomething()&lt;/code&gt; এভাবে চেইন করবেন না; শুধু নিকট বন্ধুর সাথে কথা বলুন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition over Inheritance:&lt;/strong&gt; inheritance এর গভীর hierarchy এর চেয়ে composition (ক্লাসের ভেতর অন্য ক্লাস ব্যবহার) বেশি নমনীয়।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ২.৫ Design Pattern: পরীক্ষিত সমাধানের রেসিপি
&lt;/h3&gt;

&lt;p&gt;Design pattern হলো বারবার আসা সমস্যার প্রমাণিত সমাধান। তিন ভাগে: Creational (তৈরি), Structural (সাজানো), Behavioral (আচরণ)। নিচে বাস্তবে সবচেয়ে বেশি লাগে এমনগুলো।&lt;/p&gt;

&lt;h4&gt;
  
  
  Strategy: আচরণ বদলানো যায় এমন
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; ShopEasy তে শিপিং খরচের একাধিক নিয়ম (সাধারণ, এক্সপ্রেস, ফ্রি শিপিং)।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IShippingStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StandardShipping&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IShippingStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;60m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExpressShipping&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IShippingStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;150m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FreeShipping&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IShippingStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Checkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IShippingStrategy&lt;/span&gt; &lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subtotal&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;আপনি আগে যে &lt;code&gt;IPaymentMethod&lt;/code&gt; দেখলেন, সেটাও Strategy।&lt;/p&gt;

&lt;h4&gt;
  
  
  Factory: কোন ক্লাস বানাতে হবে সেই সিদ্ধান্ত লুকানো
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShippingFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IShippingStrategy&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Subtotal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;       &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FreeShipping&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;IsExpress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExpressShipping&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;                          &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StandardShipping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Decorator: মূল কোড না ছুঁয়ে আচরণ যোগ
&lt;/h4&gt;

&lt;p&gt;ধরুন প্রতিটি repository call এ caching যোগ করতে চান, কিন্তু মূল ক্লাস বদলাতে চান না।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachedProductRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IProductRepository&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IProductRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"p:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ASP.NET Core এর &lt;strong&gt;middleware pipeline&lt;/strong&gt; ও Decorator/Chain of Responsibility এর বাস্তব উদাহরণ।&lt;/p&gt;

&lt;h4&gt;
  
  
  Observer / Event: একজনের খবরে অনেকে সাড়া দেয়
&lt;/h4&gt;

&lt;p&gt;অর্ডার হলে ইমেইল যাবে, ইনভেন্টরি কমবে, অ্যানালিটিক্স আপডেট হবে। &lt;code&gt;OrderService&lt;/code&gt; এদের কাউকে চেনে না; শুধু ঘোষণা করে "অর্ডার হয়েছে"।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEventHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendEmailHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEventHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReduceStockHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEventHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;এই ধারণাই পরে Domain Events ও Message Broker এ বড় আকারে ফিরে আসবে।&lt;/p&gt;

&lt;h4&gt;
  
  
  Repository ও Unit of Work
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; ডেটাবেস অ্যাক্সেসের লজিক একটা interface এর পেছনে লুকায়, যাতে business logic জানে না ডেটা SQL এ আছে না MongoDB তে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unit of Work:&lt;/strong&gt; একাধিক repository এর কাজ একটাই transaction এ কমিট করে (সবকিছু সফল, নয়তো কিছুই না)।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IUnitOfWork&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সতর্কতা:&lt;/strong&gt; EF Core এর &lt;code&gt;DbContext&lt;/code&gt; নিজেই Repository + Unit of Work। এর ওপর আরেকটা generic &lt;code&gt;IRepository&amp;lt;T&amp;gt;&lt;/code&gt; বসালে অনেক সময় শুধু অকারণ স্তর বাড়ে ও LINQ এর শক্তি হারায়। ছোট/মাঝারি প্রজেক্টে সরাসরি DbContext, অথবা নির্দিষ্ট ও অর্থবহ repository (যেমন &lt;code&gt;IOrderRepository.GetPendingOrders()&lt;/code&gt;) ব্যবহার করুন।&lt;/p&gt;

&lt;h4&gt;
  
  
  Singleton
&lt;/h4&gt;

&lt;p&gt;পুরো অ্যাপে একটাই instance। .NET এ নিজে static singleton লেখার বদলে DI এর &lt;code&gt;AddSingleton&lt;/code&gt; ব্যবহার করুন। Global state লুকিয়ে test কঠিন করে, তাই সাবধান।&lt;/p&gt;

&lt;h4&gt;
  
  
  Builder, Adapter, Facade (সংক্ষেপে)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Builder:&lt;/strong&gt; জটিল অবজেক্ট ধাপে ধাপে বানানো (&lt;code&gt;WebApplication.CreateBuilder&lt;/code&gt; এই pattern)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapter:&lt;/strong&gt; বেমানান interface কে মানানসই করা। যেমন তৃতীয়পক্ষের bKash SDK কে নিজেদের &lt;code&gt;IPaymentMethod&lt;/code&gt; এ মোড়ানো।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facade:&lt;/strong&gt; জটিল সাবসিস্টেমের সামনে সহজ একটা দরজা।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ২.৬ Anti-pattern: যা এড়াবেন
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Anti-pattern&lt;/th&gt;
&lt;th&gt;সমস্যা&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;God Class&lt;/td&gt;
&lt;td&gt;একটা ক্লাস সব জানে ও সব করে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anemic Domain Model&lt;/td&gt;
&lt;td&gt;Entity তে শুধু property, সব লজিক service এ (Part 3 এ আরও)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service Locator&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;serviceProvider.GetService&amp;lt;T&amp;gt;()&lt;/code&gt; ছড়িয়ে দেওয়া; dependency লুকানো থাকে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Premature Optimization&lt;/td&gt;
&lt;td&gt;প্রমাণ ছাড়াই "পারফরম্যান্সের জন্য" জটিল করা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Over-engineering&lt;/td&gt;
&lt;td&gt;৫ ইউজারের অ্যাপে ১০ স্তর&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Magic String/Number&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"bkash"&lt;/code&gt; ছড়িয়ে থাকা, constant/enum নেই&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ২.৭ Clean Code এর অভ্যাস (সংক্ষেপে)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;নাম অর্থবহ রাখুন: &lt;code&gt;GetActiveCustomers()&lt;/code&gt;, &lt;code&gt;Get()&lt;/code&gt; নয়।&lt;/li&gt;
&lt;li&gt;ফাংশন ছোট ও একটা কাজের।&lt;/li&gt;
&lt;li&gt;মন্তব্য দিয়ে খারাপ কোড ঢাকবেন না; কোডই ব্যাখ্যা করবে।&lt;/li&gt;
&lt;li&gt;Exception দিয়ে স্বাভাবিক flow control করবেন না।&lt;/li&gt;
&lt;li&gt;Nullable reference types (&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;/code&gt;) চালু রাখুন।&lt;/li&gt;
&lt;li&gt;Magic value এর বদলে &lt;code&gt;record&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt;।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 2 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SOLID মানে: ছোট দায়িত্ব, বাড়ানো সহজ, বদলানো নিরাপদ, abstraction এর ওপর নির্ভরতা।&lt;/li&gt;
&lt;li&gt;DI + interface = test করা যায় ও বদলানো যায় এমন কোড।&lt;/li&gt;
&lt;li&gt;Pattern মুখস্থ নয়, সমস্যা চিনে ব্যবহার করুন। সবচেয়ে বেশি কাজে লাগে Strategy, Factory, Decorator, Observer, Repository।&lt;/li&gt;
&lt;li&gt;YAGNI মনে রাখুন: pattern যেন জটিলতা বাড়ানোর অজুহাত না হয়।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 3: Application Architecture: কোড ও সিস্টেম কীভাবে সাজাবেন
&lt;/h2&gt;

&lt;p&gt;এখন প্রশ্ন: ShopEasy এর পুরো প্রজেক্ট কীভাবে সাজালে ১০ জনের টিম একসাথে কাজ করতে পারবে, আর ৫ বছর পরেও কোড বোঝা যাবে? উত্তর নির্ভর করে &lt;strong&gt;আপনার প্রয়োজন ও টিমের আকারের ওপর&lt;/strong&gt;। এখানে প্রতিটি style এর সুবিধা, অসুবিধা ও কখন ব্যবহার করবেন, সব থাকবে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.১ Monolith বনাম Microservices: সবচেয়ে বড় বিতর্ক
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Monolith:&lt;/strong&gt; পুরো অ্যাপ একটাই deployable ইউনিট (একটা &lt;code&gt;.exe&lt;/code&gt;/একটা docker image)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservices:&lt;/strong&gt; অনেক ছোট ছোট স্বাধীন সার্ভিস, প্রতিটার নিজস্ব ডেটাবেস ও deployment।&lt;/p&gt;

&lt;p&gt;উপমা: Monolith হলো একটা বড় রেস্টুরেন্ট যার একটাই রান্নাঘর। Microservices হলো ফুড কোর্ট যেখানে প্রতিটি দোকান আলাদা, নিজের মতো চলে, কিন্তু সমন্বয়ের ঝামেলা বেশি।&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;বিষয়&lt;/th&gt;
&lt;th&gt;Monolith&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;শুরুতে গতি&lt;/td&gt;
&lt;td&gt;খুব দ্রুত&lt;/td&gt;
&lt;td&gt;ধীর (অনেক সেটআপ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;একবারে সব&lt;/td&gt;
&lt;td&gt;প্রতিটি আলাদাভাবে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;পুরোটা একসাথে&lt;/td&gt;
&lt;td&gt;যেটার দরকার শুধু সেটা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;সহজ (একটাই process)&lt;/td&gt;
&lt;td&gt;কঠিন (distributed tracing লাগে)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data consistency&lt;/td&gt;
&lt;td&gt;সহজ (এক transaction)&lt;/td&gt;
&lt;td&gt;কঠিন (eventual consistency, Saga)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;টিম&lt;/td&gt;
&lt;td&gt;ছোট টিমের জন্য ভালো&lt;/td&gt;
&lt;td&gt;অনেক স্বাধীন টিমের জন্য&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;অপারেশন খরচ&lt;/td&gt;
&lt;td&gt;কম&lt;/td&gt;
&lt;td&gt;বেশি (Kubernetes, monitoring, CI/CD)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;একটাই stack&lt;/td&gt;
&lt;td&gt;প্রতিটি আলাদা হতে পারে&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;মূল শিক্ষা:&lt;/strong&gt; অনেক স্টার্টআপ শুরুতেই Microservices বানিয়ে "Distributed Monolith" এ পরিণত হয়, যেখানে microservices এর সব খরচ আছে কিন্তু সুবিধা নেই। &lt;strong&gt;ছোট টিম ও নতুন প্রডাক্ট হলে Modular Monolith দিয়ে শুরু করুন।&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.২ Layered (N-Tier) Architecture
&lt;/h3&gt;

&lt;p&gt;সবচেয়ে পরিচিত, সবচেয়ে পুরনো style। কোডকে স্তরে ভাগ করা:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Presentation (Controllers/UI)
        ↓
Business Logic (Services)
        ↓
Data Access (Repositories/EF Core)
        ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; ওপরের স্তর নিচেরটাকে ডাকে, উল্টোটা নয়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ShopEasy.sln
 ├─ ShopEasy.Web        (Controllers)
 ├─ ShopEasy.Business   (Services)
 ├─ ShopEasy.Data       (EF Core, Repositories)
 └─ ShopEasy.Models     (Entities, DTOs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সুবিধা:&lt;/strong&gt; সহজ, সবাই চেনে, ছোট অ্যাপে চমৎকার।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;অসুবিধা:&lt;/strong&gt; ধীরে ধীরে Business স্তর Data স্তরের ওপর নির্ভরশীল হয়ে পড়ে (EF Core এর entity সর্বত্র ছড়ায়)। ডেটাবেস বদলানো কঠিন। Domain logic ছড়িয়ে যায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৩ Clean Architecture (Onion / Hexagonal এর পরিবার)
&lt;/h3&gt;

&lt;p&gt;Layered এর সমস্যা সমাধান করতে এই পরিবারের জন্ম। তিনটি নামে (Hexagonal/Ports &amp;amp; Adapters, Onion, Clean) মূল ধারণা একই:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dependency Rule: নির্ভরতা সবসময় ভেতরের দিকে। ভেতরের স্তর বাইরের কিছু জানে না।&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;পেঁয়াজের স্তরের মতো কল্পনা করুন (ভেতর থেকে বাইরে):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt; (কেন্দ্র): Entity, business নিয়ম। কোনো framework এর ওপর নির্ভর করে না।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application:&lt;/strong&gt; Use case (যেমন "অর্ডার দাও")। Interface (port) ঘোষণা করে, যেমন &lt;code&gt;IOrderRepository&lt;/code&gt;।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt; EF Core, ইমেইল, bKash API। Application এর interface গুলো implement করে (adapter)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation/API&lt;/strong&gt; (বাইরে): Controllers/Minimal API।&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;জাদুটা কোথায়:&lt;/strong&gt; Application স্তর &lt;code&gt;IOrderRepository&lt;/code&gt; interface ঘোষণা করে, আর Infrastructure সেটা বাস্তবায়ন করে। তাই Business logic জানেই না যে SQL Server আছে। কাল MongoDB এ গেলে শুধু Infrastructure বদলাবে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ShopEasy.sln
 ├─ ShopEasy.Domain          (কোনো dependency নেই)
 ├─ ShopEasy.Application     → Domain
 ├─ ShopEasy.Infrastructure  → Application, Domain
 └─ ShopEasy.Api             → Application, Infrastructure (শুধু DI সাজানোর জন্য)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;কোড উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Domain&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;OrderStatus&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Quantity must be positive"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Confirm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Empty order"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Confirmed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Application&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Infrastructure&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EfOrderRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;লক্ষ্য করুন &lt;code&gt;Order&lt;/code&gt; এর &lt;code&gt;private set&lt;/code&gt; ও &lt;code&gt;Confirm()&lt;/code&gt; method: business নিয়ম entity এর ভেতরেই। এটাই &lt;strong&gt;Rich Domain Model&lt;/strong&gt;। বিপরীত হলো &lt;strong&gt;Anemic Model&lt;/strong&gt; (শুধু getter/setter, সব লজিক service এ), যেটা এড়ানো ভালো।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কখন Clean Architecture:&lt;/strong&gt; মাঝারি-বড় প্রজেক্ট, জটিল business logic, দীর্ঘমেয়াদি, test সহজ করতে চান।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কখন নয়:&lt;/strong&gt; ছোট CRUD অ্যাপ, প্রোটোটাইপ। অনেক boilerplate ফাইল, ছোট অ্যাপে অপ্রয়োজনীয় জটিলতা।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৪ Vertical Slice Architecture
&lt;/h3&gt;

&lt;p&gt;Layered এ একটা ফিচার (যেমন "অর্ডার দাও") ছড়িয়ে থাকে চারটা ফোল্ডারে (Controller, Service, Repository, Model)। Vertical Slice এ &lt;strong&gt;ফিচার অনুযায়ী&lt;/strong&gt; কোড সাজানো হয়, প্রতিটি slice এর সবকিছু একসাথে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Features/
 ├─ Orders/
 │   ├─ PlaceOrder/
 │   │   ├─ PlaceOrderCommand.cs
 │   │   ├─ PlaceOrderHandler.cs
 │   │   ├─ PlaceOrderValidator.cs
 │   │   └─ PlaceOrderEndpoint.cs
 │   └─ GetOrder/ ...
 └─ Products/ ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সুবিধা:&lt;/strong&gt; একটা ফিচার বদলাতে এক জায়গায় কাজ, ছোট ছোট ফিচারে আলাদা সিদ্ধান্ত (সহজ ফিচারে সরাসরি DbContext, জটিলটায় domain model) নেওয়া যায়, টিমে merge conflict কম।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;অসুবিধা:&lt;/strong&gt; slice গুলোর মধ্যে কোড ডুপ্লিকেশনের ঝুঁকি, শৃঙ্খলা না থাকলে অগোছালো।&lt;/p&gt;

&lt;p&gt;ব্যবহারিক পরামর্শ: Clean Architecture এর ভেতরে Application স্তরকে Vertical Slice এ সাজানোও খুব জনপ্রিয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৫ CQRS ও MediatR
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CQRS (Command Query Responsibility Segregation):&lt;/strong&gt; ডেটা &lt;strong&gt;পরিবর্তনের&lt;/strong&gt; মডেল (Command) ও &lt;strong&gt;পড়ার&lt;/strong&gt; মডেল (Query) আলাদা রাখা।&lt;/p&gt;

&lt;p&gt;কেন? লেখার সময় আমরা কঠোর নিয়ম, validation, transaction চাই। পড়ার সময় চাই গতি ও সুবিধাজনক আকার (যেমন একটা ড্যাশবোর্ডে ৫টা table জোড়া ডেটা)। দুটোর প্রয়োজন আলাদা।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level ১ (সহজ, প্রায় সবখানে চলে):&lt;/strong&gt; একই ডেটাবেস, কিন্তু Command ও Query এর কোড আলাদা।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level ২:&lt;/strong&gt; আলাদা read database (যেমন read এর জন্য denormalized view বা Elasticsearch)। eventual consistency আসে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Command&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;PlaceOrderCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderLineDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlaceOrderHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IUnitOfWork&lt;/span&gt; &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlaceOrderCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlaceOrderCommand&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Qty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Confirm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Query: সরাসরি projection, entity লোড না করে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;GetOrderSummaryQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderSummaryDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetOrderSummaryHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GetOrderSummaryQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderSummaryDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderSummaryDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetOrderSummaryQuery&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderSummaryDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MediatR&lt;/strong&gt; একটা জনপ্রিয় লাইব্রেরি, যা request কে সঠিক handler এ পৌঁছে দেয় ও Pipeline Behavior (validation, logging, transaction এর মতো cross-cutting কাজ) যোগ করার সুযোগ দেয়। কিছু সংস্করণ বাণিজ্যিক লাইসেন্সের আওতায় এসেছে, তাই প্রজেক্টে ব্যবহারের আগে বর্তমান লাইসেন্স নিয়ম দেখে নিন। চাইলে নিজেই সহজ dispatcher লিখতে পারেন, বা Wolverine/Mediator (source generator ভিত্তিক) এর মতো বিকল্প আছে। CQRS এর জন্য MediatR বাধ্যতামূলক নয়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pipeline Behavior: প্রতিটি command এ স্বয়ংক্রিয় validation&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidationBehavior&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TReq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TRes&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TReq&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validators&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPipelineBehavior&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TReq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TRes&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TReq&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;notnull&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TRes&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TReq&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestHandlerDelegate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TRes&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validators&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;কখন CQRS:&lt;/strong&gt; পড়া ও লেখার প্রয়োজন খুব আলাদা, জটিল domain, read-heavy সিস্টেম। &lt;strong&gt;কখন নয়:&lt;/strong&gt; সাধারণ CRUD।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৬ Domain-Driven Design (DDD)
&lt;/h3&gt;

&lt;p&gt;DDD হলো জটিল business কে কোডে সঠিকভাবে মডেল করার পদ্ধতি। মূল কথা: &lt;strong&gt;ডেভেলপার ও ব্যবসায়ী একই ভাষায় কথা বলবে, আর কোড সেই ভাষা প্রতিফলিত করবে।&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategic Design (বড় ছবি):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ubiquitous Language:&lt;/strong&gt; টিমের সবার জন্য একই শব্দভান্ডার। ব্যবসায়ী "Order" বললে কোডেও &lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;Purchase&lt;/code&gt; বা &lt;code&gt;Transaction&lt;/code&gt; নয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bounded Context:&lt;/strong&gt; একটা মডেলের সীমানা। ShopEasy তে "Product" শব্দের অর্থ ভিন্ন: Catalog এ (নাম, ছবি, বর্ণনা), Inventory তে (স্টক সংখ্যা), Shipping এ (ওজন, আকার)। প্রতিটি context এ আলাদা মডেল রাখুন, একটা বিশাল &lt;code&gt;Product&lt;/code&gt; ক্লাস নয়। &lt;strong&gt;Microservices এর সীমানা ঠিক করার সবচেয়ে ভালো দিশা এই Bounded Context।&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Map:&lt;/strong&gt; context গুলোর পারস্পরিক সম্পর্ক (কে কার ওপর নির্ভর করে)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tactical Design (কোডের building block):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Building block&lt;/th&gt;
&lt;th&gt;অর্থ&lt;/th&gt;
&lt;th&gt;উদাহরণ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entity&lt;/td&gt;
&lt;td&gt;পরিচয় (ID) আছে, সময়ের সাথে বদলায়&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;Customer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value Object&lt;/td&gt;
&lt;td&gt;পরিচয় নেই, মান দিয়ে চেনা, immutable&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Money&lt;/code&gt;, &lt;code&gt;Address&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate&lt;/td&gt;
&lt;td&gt;সম্পর্কিত object এর গুচ্ছ, একটা consistency সীমানা&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Order&lt;/code&gt; (+ তার &lt;code&gt;OrderItem&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate Root&lt;/td&gt;
&lt;td&gt;aggregate এর একমাত্র প্রবেশদ্বার&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Order&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Event&lt;/td&gt;
&lt;td&gt;ব্যবসায় যা ঘটেছে&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OrderPlaced&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain Service&lt;/td&gt;
&lt;td&gt;কোনো entity তে না ঢোকা লজিক&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PricingService&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;Aggregate root সংরক্ষণ ও আনা&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IOrderRepository&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Value Object উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="p"&gt;+(&lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Currency mismatch"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;decimal price&lt;/code&gt; এর বদলে &lt;code&gt;Money&lt;/code&gt; ব্যবহার করলে "টাকা + ডলার" যোগ করার ভুল কম্পাইলার/রানটাইমেই ধরা পড়ে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aggregate নিয়ম:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;বাইরের কেউ aggregate এর ভেতরের object সরাসরি বদলাবে না; Root এর মাধ্যমে।&lt;/li&gt;
&lt;li&gt;এক transaction এ &lt;strong&gt;একটাই&lt;/strong&gt; aggregate বদলান।&lt;/li&gt;
&lt;li&gt;aggregate গুলো একে অপরকে শুধু ID দিয়ে চেনে (পুরো object রেফারেন্স নয়)।&lt;/li&gt;
&lt;li&gt;Aggregate ছোট রাখুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Domain Event:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDomainEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDomainEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DomainEvents&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDomainEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Confirm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Confirmed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderConfirmed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// ঘোষণা, কে শুনবে জানে না&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;কখন DDD:&lt;/strong&gt; জটিল business নিয়ম (ব্যাংকিং, বীমা, লজিস্টিক)। &lt;strong&gt;কখন নয়:&lt;/strong&gt; সাধারণ CRUD বা ডেটা-কেন্দ্রিক অ্যাপ। DDD এর strategic অংশ (Ubiquitous Language, Bounded Context) প্রায় সব প্রজেক্টে কাজে লাগে, tactical অংশ শুধু জটিল domain এ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৭ Modular Monolith: বাস্তবে সবচেয়ে বুদ্ধিমান শুরু
&lt;/h3&gt;

&lt;p&gt;একটাই deployable, কিন্তু ভেতরে &lt;strong&gt;কঠোর সীমানাসহ স্বাধীন module&lt;/strong&gt; (প্রতিটি Bounded Context একটা module)।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ShopEasy (একটাই অ্যাপ)
 ├─ Modules/Catalog     (নিজের DbContext/schema, public API)
 ├─ Modules/Ordering
 ├─ Modules/Inventory
 └─ Modules/Payments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Module গুলো একে অপরের ভেতরের ক্লাস ব্যবহার করবে না; শুধু ঘোষিত public interface/contract বা event এর মাধ্যমে।&lt;/li&gt;
&lt;li&gt;প্রতিটি module এর নিজস্ব ডেটাবেস schema, অন্য module এর table এ সরাসরি JOIN নয়।&lt;/li&gt;
&lt;li&gt;Module এর মধ্যে যোগাযোগ in-process (দ্রুত, সহজ transaction)।&lt;/li&gt;
&lt;li&gt;এই নিয়ম architecture test (যেমন &lt;code&gt;NetArchTest&lt;/code&gt;) দিয়ে স্বয়ংক্রিয়ভাবে প্রয়োগ করুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;সুবিধা:&lt;/strong&gt; Monolith এর সরলতা ও Microservices এর পরিষ্কার সীমানা, দুটোই। ভবিষ্যতে কোনো module আলাদা service এ সরানো সহজ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৮ Microservices বিস্তারিত
&lt;/h3&gt;

&lt;p&gt;যখন প্রয়োজন সত্যিই আছে (বড় টিম, আলাদা scaling, স্বাধীন deployment):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ভালো microservice এর বৈশিষ্ট্য:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;একটা Bounded Context (ব্যবসায়িক সামর্থ্য) এর মালিক&lt;/li&gt;
&lt;li&gt;নিজস্ব ডেটাবেস (&lt;strong&gt;Database per Service&lt;/strong&gt;; অন্যের DB তে ঢোকা নিষিদ্ধ)&lt;/li&gt;
&lt;li&gt;স্বাধীনভাবে deploy ও scale&lt;/li&gt;
&lt;li&gt;ছোট API/contract এর মাধ্যমে যোগাযোগ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;যোগাযোগের ধরন:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ধরন&lt;/th&gt;
&lt;th&gt;কী&lt;/th&gt;
&lt;th&gt;কখন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Synchronous (HTTP/gRPC)&lt;/td&gt;
&lt;td&gt;সরাসরি call, উত্তরের জন্য অপেক্ষা&lt;/td&gt;
&lt;td&gt;তাৎক্ষণিক উত্তর লাগে (যেমন দাম জানা)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asynchronous (Message/Event)&lt;/td&gt;
&lt;td&gt;বার্তা পাঠিয়ে এগিয়ে যাওয়া&lt;/td&gt;
&lt;td&gt;ফলাফলের জন্য অপেক্ষা না করলেও চলে (ইমেইল, ইনভেন্টরি আপডেট)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Microservices এর লুকানো খরচ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network failure, latency, retry, timeout (Part 7)&lt;/li&gt;
&lt;li&gt;Distributed transaction (Saga, Part 6)&lt;/li&gt;
&lt;li&gt;Distributed tracing ও কেন্দ্রীয় logging (Part 9)&lt;/li&gt;
&lt;li&gt;সংস্করণ সামঞ্জস্য (contract versioning)&lt;/li&gt;
&lt;li&gt;বেশি infrastructure ও DevOps দক্ষতা&lt;/li&gt;
&lt;li&gt;Testing এর জটিলতা (contract test, integration test)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Microservices এ যাওয়ার আগে চেকলিস্ট:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] টিম কি একাধিক স্বাধীন দলে ভাগ হয়েছে?&lt;/li&gt;
&lt;li&gt;[ ] আলাদা অংশের scaling এর প্রয়োজন সত্যিই ভিন্ন?&lt;/li&gt;
&lt;li&gt;[ ] Monolith এর deployment কি টিমের গতির বাধা?&lt;/li&gt;
&lt;li&gt;[ ] CI/CD, monitoring, container এর পরিপক্কতা আছে?&lt;/li&gt;
&lt;li&gt;[ ] Bounded Context পরিষ্কারভাবে চেনা গেছে?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;একটাও "না" হলে সম্ভবত এখনও সময় হয়নি।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strangler Fig Pattern:&lt;/strong&gt; পুরনো Monolith একবারে ফেলে নতুন বানানোর (Big Bang rewrite) ঝুঁকি এড়াতে ধীরে ধীরে একটা একটা ফিচার নতুন service এ সরান, সামনে একটা proxy/gateway দিয়ে ট্রাফিক ভাগ করুন, শেষে পুরনোটা আপনাআপনি ফাঁকা হয়ে যায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.৯ Event-Driven Architecture (EDA)
&lt;/h3&gt;

&lt;p&gt;সার্ভিসগুলো সরাসরি একে অপরকে call না করে &lt;strong&gt;event&lt;/strong&gt; প্রকাশ করে; আগ্রহীরা শোনে ও সাড়া দেয়।&lt;/p&gt;

&lt;p&gt;ShopEasy তে: &lt;code&gt;Ordering&lt;/code&gt; প্রকাশ করল &lt;code&gt;OrderPlaced&lt;/code&gt;। &lt;code&gt;Inventory&lt;/code&gt; শুনে স্টক কমাল, &lt;code&gt;Notification&lt;/code&gt; ইমেইল পাঠাল, &lt;code&gt;Analytics&lt;/code&gt; হিসাব রাখল। Ordering এদের কাউকে চেনেই না, নতুন কেউ যোগ হলেও Ordering বদলাতে হয় না (loose coupling)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event এর ধরন:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event Notification:&lt;/strong&gt; "কিছু ঘটেছে" (ছোট বার্তা, দরকারে শ্রোতা জিজ্ঞেস করবে)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-Carried State Transfer:&lt;/strong&gt; event এ প্রয়োজনীয় ডেটাও থাকে (শ্রোতার আর ফেরত call লাগে না)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Sourcing:&lt;/strong&gt; বর্তমান অবস্থা না রেখে &lt;strong&gt;ঘটনার ধারাবাহিক তালিকা&lt;/strong&gt; রাখা; যেকোনো সময়ের অবস্থা ঘটনাগুলো আবার চালিয়ে বের করা যায় (ব্যাংকের লেজারের মতো)। অডিট ও ইতিহাসে চমৎকার, কিন্তু জটিল; সবার জন্য নয়।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;সুবিধা:&lt;/strong&gt; loose coupling, সহজে নতুন কনজিউমার, প্রবল scalability। &lt;strong&gt;অসুবিধা:&lt;/strong&gt; প্রবাহ বোঝা ও debug কঠিন, eventual consistency, বার্তার ক্রম ও ডুপ্লিকেট সামলানো (Part 6)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.১০ Serverless ও অন্যান্য style
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless (Azure Functions, AWS Lambda):&lt;/strong&gt; server পরিচালনা না করে শুধু ফাংশন চালান, ব্যবহার অনুযায়ী বিল। সুবিধা: শূন্য ট্রাফিকে খরচ শূন্য, স্বয়ংক্রিয় scale। অসুবিধা: cold start, চলার সময়সীমা, vendor lock-in, স্থানীয় debug কঠিন। event-triggered কাজে (ছবি resize, scheduled job) দারুণ।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SOA (Service-Oriented Architecture):&lt;/strong&gt; Microservices এর পূর্বসূরি; বড় ও কেন্দ্রীয় bus (ESB) নির্ভর।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microkernel / Plugin:&lt;/strong&gt; ছোট কোর + প্লাগইন (VS Code এর মতো)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend for Frontend (BFF):&lt;/strong&gt; প্রতিটি ক্লায়েন্টের (web, mobile) জন্য আলাদা ছোট backend, যা ক্লায়েন্টের প্রয়োজনমতো ডেটা সাজায়।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৩.১১ কোন architecture কখন: সিদ্ধান্তের গাইড
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;পরিস্থিতি&lt;/th&gt;
&lt;th&gt;সুপারিশ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;১-৩ জনের টিম, নতুন প্রডাক্ট, MVP&lt;/td&gt;
&lt;td&gt;সরল Layered বা Vertical Slice Monolith&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;সাধারণ CRUD, অ্যাডমিন প্যানেল&lt;/td&gt;
&lt;td&gt;Layered, ন্যূনতম pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;জটিল business, দীর্ঘমেয়াদি&lt;/td&gt;
&lt;td&gt;Clean Architecture + DDD (tactical)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;৫-২০ জন, একাধিক ডোমেইন&lt;/td&gt;
&lt;td&gt;Modular Monolith&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;২০+ জন, স্বাধীন টিম, ভিন্ন scaling&lt;/td&gt;
&lt;td&gt;Microservices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ফাইল/ইভেন্ট ট্রিগার করা ছোট কাজ&lt;/td&gt;
&lt;td&gt;Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read ও write এর চাহিদা অনেক আলাদা&lt;/td&gt;
&lt;td&gt;CQRS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;অডিট/ইতিহাস অপরিহার্য&lt;/td&gt;
&lt;td&gt;Event Sourcing (সতর্কভাবে)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;সোনালি নিয়ম:&lt;/strong&gt; সবচেয়ে সরল যা প্রয়োজন মেটায় তা দিয়ে শুরু করুন। যখন ব্যথা অনুভব করবেন, তখনই জটিলতা যোগ করুন, আগে নয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৩.১২ ShopEasy তে প্রয়োগ
&lt;/h3&gt;

&lt;p&gt;রাফির টিম এখন ৮ জন। সিদ্ধান্ত: Microservices নয়, &lt;strong&gt;Modular Monolith&lt;/strong&gt; (Catalog, Ordering, Inventory, Payments)। প্রতিটি module Clean Architecture ধাঁচে, জটিল Ordering এ DDD, রিপোর্টিং এ CQRS এর পড়ার দিক। Module এর মধ্যে যোগাযোগ interface ও in-process event দিয়ে। এতে সবার কাজ আলাদা, ডেপ্লয় সহজ, আর ভবিষ্যতে Payments আলাদা service করা যাবে।&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Architecture মানে trade-off; "সেরা" নেই, "উপযুক্ত" আছে।&lt;/li&gt;
&lt;li&gt;Layered সহজ; Clean Architecture দীর্ঘমেয়াদি test-যোগ্য কোডের জন্য; Vertical Slice ফিচার-কেন্দ্রিক।&lt;/li&gt;
&lt;li&gt;CQRS: পড়া-লেখার মডেল আলাদা; DDD: জটিল business মডেল করা; Bounded Context: সীমানা।&lt;/li&gt;
&lt;li&gt;Modular Monolith বেশিরভাগ ক্ষেত্রে সবচেয়ে বাস্তবসম্মত শুরু।&lt;/li&gt;
&lt;li&gt;Microservices এর সুবিধা বাস্তব, কিন্তু খরচও বিশাল; প্রয়োজন প্রমাণিত হলে তবেই।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 4: Database Design: ডেটা সংরক্ষণ ও দ্রুত অ্যাক্সেস
&lt;/h2&gt;

&lt;p&gt;বাস্তবে বেশিরভাগ system এর বাধা application কোড নয়, &lt;strong&gt;database&lt;/strong&gt;। ShopEasy এর পেজ ধীর হওয়ার ৮০% কারণ ছিল খারাপ query ও index এর অভাব। এই Part সেই জায়গায় আপনাকে শক্ত করবে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.১ SQL বনাম NoSQL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Relational (SQL):&lt;/strong&gt; table, row, column, সম্পর্ক (foreign key), শক্তিশালী transaction। (SQL Server, PostgreSQL, MySQL)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NoSQL:&lt;/strong&gt; একাধিক ধরন:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ধরন&lt;/th&gt;
&lt;th&gt;উদাহরণ&lt;/th&gt;
&lt;th&gt;কখন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Document&lt;/td&gt;
&lt;td&gt;MongoDB, Cosmos DB&lt;/td&gt;
&lt;td&gt;নমনীয় কাঠামো, product catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key-Value&lt;/td&gt;
&lt;td&gt;Redis, DynamoDB&lt;/td&gt;
&lt;td&gt;cache, session, দ্রুত lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wide-Column&lt;/td&gt;
&lt;td&gt;Cassandra&lt;/td&gt;
&lt;td&gt;বিপুল লেখা, time-series&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graph&lt;/td&gt;
&lt;td&gt;Neo4j&lt;/td&gt;
&lt;td&gt;সম্পর্ক-কেন্দ্রিক (বন্ধুর বন্ধু)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;full-text search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;সিদ্ধান্তের নিয়ম:&lt;/strong&gt; সন্দেহ হলে &lt;strong&gt;Relational দিয়ে শুরু করুন&lt;/strong&gt; (PostgreSQL বা SQL Server)। সম্পর্ক, transaction ও আড-হক query এ এর শক্তি অসাধারণ। NoSQL নিন যখন নির্দিষ্ট প্রয়োজন আছে (বিপুল scale, নমনীয় schema, বিশেষ ডেটা মডেল)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polyglot Persistence:&lt;/strong&gt; একই সিস্টেমে ভিন্ন কাজে ভিন্ন DB। ShopEasy: অর্ডার ও পেমেন্ট SQL এ, পণ্য সার্চ Elasticsearch এ, cart ও session Redis এ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.২ ডেটাবেস ডিজাইনের মূলনীতি
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Normalization
&lt;/h4&gt;

&lt;p&gt;ডুপ্লিকেট ডেটা সরানোর প্রক্রিয়া।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;খারাপ (সব একটা table এ):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OrderId&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;th&gt;CustomerPhone&lt;/th&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;রাফি&lt;/td&gt;
&lt;td&gt;0171...&lt;/td&gt;
&lt;td&gt;Phone&lt;/td&gt;
&lt;td&gt;20000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;রাফি&lt;/td&gt;
&lt;td&gt;0171...&lt;/td&gt;
&lt;td&gt;Case&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;রাফির ফোন বদলালে সব row আপডেট করতে হবে; একটা ভুলে গেলে ডেটা অসঙ্গত।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ভালো:&lt;/strong&gt; &lt;code&gt;Customers&lt;/code&gt;, &lt;code&gt;Orders&lt;/code&gt;, &lt;code&gt;OrderItems&lt;/code&gt;, &lt;code&gt;Products&lt;/code&gt; আলাদা table, ID দিয়ে সংযুক্ত।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1NF:&lt;/strong&gt; প্রতিটি cell এ একটাই মান (কমা দিয়ে একাধিক মান নয়)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2NF:&lt;/strong&gt; Composite key এর অংশের ওপর আংশিক নির্ভরতা নেই&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3NF:&lt;/strong&gt; Non-key column শুধু key এর ওপর নির্ভর করে, অন্য non-key এর ওপর নয়&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Denormalization
&lt;/h4&gt;

&lt;p&gt;পড়ার গতির জন্য ইচ্ছাকৃতভাবে কিছু ডুপ্লিকেট রাখা (যেমন Order এ &lt;code&gt;CustomerName&lt;/code&gt; এর কপি)। Read-heavy সিস্টেমে কাজে লাগে, তবে সংগতি রাখার দায় আপনার। &lt;strong&gt;নিয়ম:&lt;/strong&gt; আগে normalize করুন, প্রমাণিত দরকারে denormalize।&lt;/p&gt;

&lt;h4&gt;
  
  
  Primary Key এর পছন্দ
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-increment int/bigint:&lt;/strong&gt; সহজ, ছোট, দ্রুত; কিন্তু আন্দাজযোগ্য (&lt;code&gt;/orders/5&lt;/code&gt; এর পর &lt;code&gt;/orders/6&lt;/code&gt;), একাধিক DB তে মেলানো কঠিন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GUID (UUID):&lt;/strong&gt; distributed এ স্বাধীনভাবে তৈরি করা যায়; কিন্তু random GUID clustered index এ page split ঘটিয়ে ধীর করে। সমাধান: sequential GUID (.NET 9 এ &lt;code&gt;Guid.CreateVersion7()&lt;/code&gt;) বা ULID।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ডেটা নিরাপত্তায়:&lt;/strong&gt; বাইরের API তে ধারাবাহিক ID প্রকাশ করলে ডেটা গণনা করা সহজ হয়; প্রয়োজনে public ID আলাদা রাখুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Data Type বাছাই
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;টাকা: &lt;code&gt;decimal&lt;/code&gt; (কখনোই &lt;code&gt;float/double&lt;/code&gt; নয়, ভগ্নাংশ ভুল হয়)&lt;/li&gt;
&lt;li&gt;সময়: UTC তে &lt;code&gt;DateTimeOffset&lt;/code&gt;/&lt;code&gt;datetime2&lt;/code&gt; সংরক্ষণ করুন&lt;/li&gt;
&lt;li&gt;Unicode (বাংলা) এর জন্য &lt;code&gt;nvarchar&lt;/code&gt; (SQL Server) বা UTF-8 (PostgreSQL)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৪.৩ Index: দ্রুত query এর চাবিকাঠি
&lt;/h3&gt;

&lt;p&gt;বইয়ের শেষের সূচিপত্র (index) এর মতো। সূচি না থাকলে একটা শব্দ খুঁজতে পুরো বই পড়তে হয় (&lt;strong&gt;Full Table Scan&lt;/strong&gt;, O(n))। সূচি থাকলে সরাসরি পাতায় যাওয়া যায় (B-Tree, O(log n))।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ১০ লক্ষ order এ এই query ধীর (scan)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- index দিলে দ্রুত (seek)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;IX_Orders_CustomerId&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Index এর ধরন:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clustered:&lt;/strong&gt; ডেটা যে ক্রমে ডিস্কে সাজানো (টেবিলে একটাই; সাধারণত PK)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-clustered:&lt;/strong&gt; আলাদা সূচি যা মূল ডেটার দিকে ইঙ্গিত করে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite:&lt;/strong&gt; একাধিক column (ক্রম গুরুত্বপূর্ণ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Covering:&lt;/strong&gt; query এর সব দরকারি column index এই থাকে (&lt;code&gt;INCLUDE&lt;/code&gt;), মূল table এ যেতে হয় না&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique, Filtered/Partial:&lt;/strong&gt; বিশেষ ক্ষেত্রে&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Composite index এর leftmost নিয়ম:&lt;/strong&gt; &lt;code&gt;(CustomerId, Status)&lt;/code&gt; index টি &lt;code&gt;WHERE CustomerId = ?&lt;/code&gt; ও &lt;code&gt;WHERE CustomerId = ? AND Status = ?&lt;/code&gt; এ কাজ করে, কিন্তু শুধু &lt;code&gt;WHERE Status = ?&lt;/code&gt; এ কাজে লাগে না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index এর দাম:&lt;/strong&gt; প্রতিটি index পড়া দ্রুত করে, কিন্তু লেখা (INSERT/UPDATE/DELETE) ধীর ও জায়গা বেশি লাগায়। তাই সব column এ index দেবেন না। যে column গুলো &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt; এ ঘনঘন আসে, সেগুলোতে দিন।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index নষ্ট করে যে ভুলগুলো:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nb"&gt;YEAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;     &lt;span class="c1"&gt;-- column এ function: index কাজ করে না&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%rafi'&lt;/span&gt;          &lt;span class="c1"&gt;-- শুরুতে wildcard: scan&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1711111111&lt;/span&gt;         &lt;span class="c1"&gt;-- type mismatch (string কে number এর সাথে)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ঠিক করুন: &lt;code&gt;WHERE CreatedAt &amp;gt;= '2026-01-01' AND CreatedAt &amp;lt; '2027-01-01'&lt;/code&gt;।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution Plan&lt;/strong&gt; পড়তে শিখুন (SSMS এ "Include Actual Execution Plan", PostgreSQL এ &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;)। "Index Seek" ভালো, "Table Scan" ছোট table ছাড়া সন্দেহজনক।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.৪ Transaction ও ACID
&lt;/h3&gt;

&lt;p&gt;উপমা: ব্যাংক ট্রান্সফার। ১০০০ টাকা A থেকে কেটে B তে যোগ হবে। মাঝপথে সার্ভার ক্র্যাশ করলে? টাকা উধাও হওয়া চলবে না।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Atomicity:&lt;/strong&gt; সব সফল নয়তো কিছুই নয়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; ডেটা সবসময় বৈধ অবস্থা থেকে বৈধ অবস্থায় যায় (constraint মানা হয়)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; একসাথে চলা transaction একে অপরকে বিভ্রান্ত করে না&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durability:&lt;/strong&gt; কমিট হলে ক্র্যাশেও ডেটা থাকে
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransactionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt;   &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RollbackAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;SaveChangesAsync()&lt;/code&gt; একা নিজেই একটা transaction।)&lt;/p&gt;

&lt;h4&gt;
  
  
  Isolation Level ও সমস্যা
&lt;/h4&gt;

&lt;p&gt;একসাথে অনেকে ডেটা বদলালে যা ঘটতে পারে:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dirty Read:&lt;/strong&gt; কমিট না হওয়া ডেটা পড়া&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-repeatable Read:&lt;/strong&gt; একই transaction এ দুবার পড়ে ভিন্ন মান&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phantom Read:&lt;/strong&gt; একই query তে দ্বিতীয়বার নতুন row দেখা যায়&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Dirty&lt;/th&gt;
&lt;th&gt;Non-repeatable&lt;/th&gt;
&lt;th&gt;Phantom&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read Uncommitted&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Committed (সাধারণ default)&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeatable Read&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;সম্ভব&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serializable&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;td&gt;না&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;উচ্চ isolation নিরাপদ, কিন্তু ধীর ও deadlock এর ঝুঁকি বেশি। SQL Server এ &lt;strong&gt;Read Committed Snapshot Isolation (RCSI)&lt;/strong&gt; ও PostgreSQL এ MVCC পড়াকে লেখা থেকে আলাদা রেখে ভালো পারফরম্যান্স দেয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.৫ Concurrency নিয়ন্ত্রণ: ShopEasy এর "শেষ ১টা পণ্য" সমস্যা
&lt;/h3&gt;

&lt;p&gt;দুজন একসাথে শেষ ফোনটা কিনছে। দুজনেই দেখল স্টক = ১। দুজনেই অর্ডার করল। স্টক হলো -১! একে &lt;strong&gt;Lost Update / Race Condition&lt;/strong&gt; বলে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;১. Pessimistic Locking:&lt;/strong&gt; পড়ার সময়ই row লক করা; অন্যরা অপেক্ষা করে। নিরাপদ, কিন্তু ধীর ও deadlock ঝুঁকি।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;২. Optimistic Concurrency (EF Core এ জনপ্রিয়):&lt;/strong&gt; লক নেই; সংরক্ষণের সময় দেখা হয় অন্য কেউ ইতোমধ্যে বদলেছে কি না।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Stock&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;RowVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;   &lt;span class="c1"&gt;// SQL Server&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stock&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbUpdateConcurrencyException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// কেউ আগে বদলে ফেলেছে; আবার পড়ে পুনরায় চেষ্টা করুন, বা ব্যবহারকারীকে জানান&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;৩. Atomic আপডেট:&lt;/strong&gt; সবচেয়ে সহজ ও কার্যকর অনেক সময়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stock&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteUpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stock&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Conflict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Out of stock"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;এক SQL এ "যদি স্টক থাকে, তবে কমাও", ফলে দুজন একসাথে পারে না।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.৬ ORM: EF Core বনাম Dapper
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;বিষয়&lt;/th&gt;
&lt;th&gt;EF Core&lt;/th&gt;
&lt;th&gt;Dapper&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ধরন&lt;/td&gt;
&lt;td&gt;পূর্ণ ORM&lt;/td&gt;
&lt;td&gt;Micro-ORM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL লেখা&lt;/td&gt;
&lt;td&gt;LINQ দিয়ে&lt;/td&gt;
&lt;td&gt;নিজে লেখেন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change tracking&lt;/td&gt;
&lt;td&gt;আছে&lt;/td&gt;
&lt;td&gt;নেই&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration&lt;/td&gt;
&lt;td&gt;আছে&lt;/td&gt;
&lt;td&gt;নেই&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;গতি&lt;/td&gt;
&lt;td&gt;ভালো (সঠিক ব্যবহারে)&lt;/td&gt;
&lt;td&gt;সর্বোচ্চ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;কখন&lt;/td&gt;
&lt;td&gt;সাধারণ CRUD, domain model&lt;/td&gt;
&lt;td&gt;জটিল রিপোর্ট, গতি-সংবেদনশীল query&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;বাস্তব পরামর্শ:&lt;/strong&gt; একসাথে দুটো ব্যবহার করা যায়: লেখার জন্য EF Core, জটিল read query তে Dapper বা raw SQL।&lt;/p&gt;

&lt;h4&gt;
  
  
  EF Core এর সাধারণ সমস্যা ও সমাধান
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;১. N+1 Query সমস্যা (সবচেয়ে কুখ্যাত):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ ১টা query orders এর জন্য + প্রতিটি order এর জন্য আরও ১টা = N+1&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// lazy loading এ প্রতিবার DB hit&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ একটা query তে&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ আরও ভালো: শুধু যা দরকার&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;১০০০ অর্ডার হলে আগেরটা ১০০১টা DB call; পরেরটা ১টা।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;২. শুধু পড়ার জন্য &lt;code&gt;AsNoTracking()&lt;/code&gt;:&lt;/strong&gt; change tracking এর খরচ বাঁচে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৩. পুরো entity না এনে &lt;code&gt;Select&lt;/code&gt; projection:&lt;/strong&gt; ১০টা column লাগলে ৫০টা আনবেন না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৪. Pagination ছাড়া সব ডেটা আনা:&lt;/strong&gt; &lt;code&gt;.Take()/.Skip()&lt;/code&gt; বা Keyset pagination ব্যবহার করুন (Part 5)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৫. &lt;code&gt;ToList()&lt;/code&gt; আগে filter পরে:&lt;/strong&gt; ❌ &lt;code&gt;db.Orders.ToList().Where(...)&lt;/code&gt; পুরো table মেমরিতে আনে। ✅ &lt;code&gt;Where&lt;/code&gt; আগে, &lt;code&gt;ToList&lt;/code&gt; শেষে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৬. Cartesian Explosion:&lt;/strong&gt; একাধিক collection এ &lt;code&gt;Include&lt;/code&gt; করলে row সংখ্যা গুণ হয়। সমাধান &lt;code&gt;AsSplitQuery()&lt;/code&gt;।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৭. Compiled Query, Bulk অপারেশন:&lt;/strong&gt; &lt;code&gt;ExecuteUpdate&lt;/code&gt;/&lt;code&gt;ExecuteDelete&lt;/code&gt; দিয়ে বড় আপডেট; বিপুল insert এ &lt;code&gt;EFCore.BulkExtensions&lt;/code&gt; জাতীয় লাইব্রেরি।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;৮. Migration:&lt;/strong&gt; schema পরিবর্তন কোডে ভার্সন করুন (&lt;code&gt;dotnet ef migrations add ...&lt;/code&gt;)। Production এ সরাসরি অ্যাপ চালু হওয়ার সময় migrate না করে CI/CD pipeline এ আলাদা ধাপ বা script হিসেবে চালানো নিরাপদ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.৭ বড় scale এর কৌশল
&lt;/h3&gt;

&lt;p&gt;ShopEasy তে ডেটা ও ট্রাফিক বাড়ছে; এক DB সার্ভার হাঁপিয়ে উঠছে। ধাপে ধাপে:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ধাপ ১: Query ও Index ঠিক করুন, Cache যোগ করুন।&lt;/strong&gt; (৯০% সমস্যা এখানেই মেটে।)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ধাপ ২: Vertical scaling:&lt;/strong&gt; বড় সার্ভার।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ধাপ ৩: Read Replica (Replication):&lt;/strong&gt; মূল DB (Primary) তে লেখা, কপি (Replica) গুলোতে পড়া।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary → Replica এ ডেটা অনুলিপি হয় (সাধারণত async)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replication lag:&lt;/strong&gt; সাময়িক পুরনো ডেটা (eventual consistency)&lt;/li&gt;
&lt;li&gt;Read-heavy অ্যাপে (বেশিরভাগ ওয়েব) বিরাট সুবিধা&lt;/li&gt;
&lt;li&gt;Primary ফেল করলে Replica কে Primary বানানো যায় (failover, high availability)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// পড়ার জন্য আলাদা connection string&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WriteDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primaryConn&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replicaConn&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(এটাই CQRS এর বাস্তব ব্যবহারের একটা রূপ।)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ধাপ ৪: Partitioning:&lt;/strong&gt; একটা বিশাল table কে ছোট ভাগে ভাঙা (একই DB সার্ভারে)। যেমন &lt;code&gt;Orders&lt;/code&gt; কে মাস অনুযায়ী ভাগ। পুরনো ডেটা আর্কাইভ সহজ, query দ্রুত।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ধাপ ৫: Sharding (Horizontal Partitioning):&lt;/strong&gt; ডেটা একাধিক &lt;strong&gt;আলাদা DB সার্ভারে&lt;/strong&gt; ভাগ। যেমন ইউজার ID অনুযায়ী: ১-১০ লক্ষ Shard-A তে, ১০-২০ লক্ষ Shard-B তে।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shard key&lt;/strong&gt; বাছাই সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্ত; খারাপ key এ কিছু shard এ চাপ জমে (hot shard)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range-based:&lt;/strong&gt; ID এর পরিসর অনুযায়ী (সহজ, কিন্তু hot spot ঝুঁকি)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash-based:&lt;/strong&gt; ID এর hash দিয়ে (সমানভাবে ছড়ায়, কিন্তু range query কঠিন)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory-based:&lt;/strong&gt; একটা lookup table বলে দেয় কোন ডেটা কোথায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Hashing:&lt;/strong&gt; shard যোগ/বাদ দিলে কম ডেটা সরাতে হয়&lt;/li&gt;
&lt;li&gt;দাম: shard পেরিয়ে JOIN ও transaction কঠিন বা অসম্ভব; জটিলতা বিপুল&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;সোনালি নিয়ম:&lt;/strong&gt; Sharding শেষ অস্ত্র। প্রথমে index, cache, replica, partitioning ও ডেটা আর্কাইভ সব চেষ্টা করুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৪.৮ ডেটা আর্কাইভ, Soft Delete ও Audit
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Soft Delete:&lt;/strong&gt; &lt;code&gt;IsDeleted&lt;/code&gt; ফ্ল্যাগ; EF Core এর Global Query Filter দিয়ে স্বয়ংক্রিয়ভাবে লুকানো যায়। গোপনীয়তা আইনে (যেমন ইউজার ডেটা মুছে ফেলার অনুরোধ) সতর্ক থাকুন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Trail:&lt;/strong&gt; কে, কখন, কী বদলাল। &lt;code&gt;CreatedAt/By&lt;/code&gt;, &lt;code&gt;UpdatedAt/By&lt;/code&gt; বা আলাদা audit table। SQL Server এ Temporal Table আছে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ডেটা সংরক্ষণ নীতি (Retention):&lt;/strong&gt; পুরনো লগ/ডেটা কতদিন রাখবেন তা ঠিক করুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৪.৯ Backup, Recovery ও Migration কৌশল
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RPO (Recovery Point Objective):&lt;/strong&gt; সর্বোচ্চ কতটুকু ডেটা হারানো সহ্য করবেন (যেমন ৫ মিনিট)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTO (Recovery Time Objective):&lt;/strong&gt; কত সময়ে পুনরায় চালু করতে হবে (যেমন ১ ঘণ্টা)&lt;/li&gt;
&lt;li&gt;নিয়মিত backup নিন এবং &lt;strong&gt;restore পরীক্ষা করুন&lt;/strong&gt;। যে backup কখনো restore করে দেখা হয়নি, সেটা backup নয়, আশা।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-downtime migration:&lt;/strong&gt; Expand → Migrate → Contract পদ্ধতি। নতুন column আগে যোগ করুন (নতুন ও পুরনো কোড দুটোই চলে), ডেটা সরান, শেষে পুরনোটা সরান। এক ধাপে column এর নাম বদলালে ডেপ্লয়ের সময় পুরনো কোড ভাঙে।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 4 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;সন্দেহে Relational; প্রয়োজন বুঝে NoSQL; Polyglot হতে পারে।&lt;/li&gt;
&lt;li&gt;Index পড়া দ্রুত করে কিন্তু লেখা ধীর করে; execution plan পড়ুন।&lt;/li&gt;
&lt;li&gt;Concurrency সামলান: atomic update বা optimistic concurrency।&lt;/li&gt;
&lt;li&gt;N+1, &lt;code&gt;AsNoTracking&lt;/code&gt;, projection, pagination: EF Core এর মৌলিক পারফরম্যান্স অভ্যাস।&lt;/li&gt;
&lt;li&gt;Scale এর ক্রম: query/index → cache → replica → partition → shard।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 5: API Design: সিস্টেমের মুখ
&lt;/h2&gt;

&lt;p&gt;API হলো আপনার সিস্টেমের সাথে বাইরের জগতের চুক্তি। একবার প্রকাশ করলে বদলানো কঠিন, কারণ মোবাইল অ্যাপ, পার্টনার সবাই এর ওপর নির্ভর করে। তাই শুরু থেকেই ভালোভাবে ডিজাইন করুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.১ REST এর মূলকথা
&lt;/h3&gt;

&lt;p&gt;REST একটা architectural style: &lt;strong&gt;resource কেন্দ্রিক&lt;/strong&gt;। সবকিছু একটা "জিনিস" (noun), তার ওপর HTTP method দিয়ে কাজ।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ভালো URL ডিজাইন:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;কাজ&lt;/th&gt;
&lt;th&gt;❌ খারাপ&lt;/th&gt;
&lt;th&gt;✅ ভালো&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;সব অর্ডার&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /getAllOrders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /orders&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;একটা অর্ডার&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /getOrder?id=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /orders/5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;নতুন অর্ডার&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /createOrder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /orders&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;আপডেট&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /updateOrder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT /orders/5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;মুছুন&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /deleteOrder/5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /orders/5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;অর্ডারের আইটেম&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /getOrderItems/5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /orders/5/items&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; বহুবচন noun, ক্রিয়া নয়; ছোট হাতের অক্ষর ও hyphen; নেস্টিং ২ স্তরের বেশি নয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;সঠিক Status Code ফেরত দিন:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;201 Created&lt;/code&gt; + &lt;code&gt;Location&lt;/code&gt; header নতুন resource তৈরিতে&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;204 No Content&lt;/code&gt; সফল কিন্তু ফেরত দেওয়ার কিছু নেই&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt; ভুল ইনপুট, &lt;code&gt;404&lt;/code&gt; পাওয়া যায়নি, &lt;code&gt;409&lt;/code&gt; সংঘর্ষ (conflict), &lt;code&gt;422&lt;/code&gt; যাচাই ব্যর্থ&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401&lt;/code&gt; পরিচয় নেই (login করেনি), &lt;code&gt;403&lt;/code&gt; পরিচয় আছে কিন্তু অনুমতি নেই&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt; অনেক বেশি request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;৫০০ কখনো নিজে ছুড়বেন না কোনো স্বাভাবিক ব্যবসায়িক ভুলের জন্য।&lt;/strong&gt; ৫০০ মানে "আমাদের সার্ভারে সমস্যা"।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.২ ASP.NET Core তে API: Minimal API বনাম Controller
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Minimal API (আধুনিক, কম boilerplate):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/orders"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{id:int}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMediator&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GetOrderSummaryQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlaceOrderCommand&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMediator&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/api/v1/orders/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controller:&lt;/strong&gt; বড় প্রজেক্টে সুপরিচিত কাঠামো, filter, model binding এর সুবিধা।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/v1/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMediator&lt;/span&gt; &lt;span class="n"&gt;mediator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id:int}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderSummaryDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;mediator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GetOrderSummaryQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;দুটোই ঠিক; টিমের পছন্দ ও প্রজেক্ট বুঝে বেছে নিন। &lt;strong&gt;&lt;code&gt;CancellationToken&lt;/code&gt;&lt;/strong&gt; সবসময় পাস করুন: ক্লায়েন্ট সংযোগ ছেড়ে দিলে সার্ভারের কাজ থামে, সম্পদ বাঁচে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৩ DTO: Entity সরাসরি বাইরে দেবেন না
&lt;/h3&gt;

&lt;p&gt;Database entity কখনো API response বা request হিসেবে সরাসরি ব্যবহার করবেন না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কারণ:&lt;/strong&gt; ভেতরের কাঠামো ফাঁস, গোপন ফিল্ড (যেমন &lt;code&gt;PasswordHash&lt;/code&gt;) বেরিয়ে যাওয়ার ঝুঁকি, &lt;strong&gt;Mass Assignment আক্রমণ&lt;/strong&gt; (ক্লায়েন্ট &lt;code&gt;IsAdmin=true&lt;/code&gt; পাঠিয়ে দিল), আর DB বদলালে API ভাঙা।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Request DTO: শুধু যা ইউজার পাঠাতে পারবে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;CreateProductRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Response DTO: শুধু যা দেখানো উচিত&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৫.৪ Validation ও Error Response
&lt;/h3&gt;

&lt;p&gt;ইনপুট কখনো বিশ্বাস করবেন না। &lt;strong&gt;FluentValidation&lt;/strong&gt; বা &lt;code&gt;DataAnnotations&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateProductValidator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateProductRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CreateProductValidator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;MaximumLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error এর আদর্শ ফরম্যাট:&lt;/strong&gt; RFC 9457 &lt;strong&gt;Problem Details&lt;/strong&gt; (.NET এ built-in):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddProblemDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// IExceptionHandler দিয়ে কাস্টমাইজ করুন&lt;/span&gt;

&lt;span class="c1"&gt;// ফল:&lt;/span&gt;
&lt;span class="c1"&gt;// { "type": "...", "title": "Validation failed", "status": 422,&lt;/span&gt;
&lt;span class="c1"&gt;//   "errors": { "Price": ["must be greater than 0"] }, "traceId": "..." }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Global Exception Handler:&lt;/strong&gt; প্রতিটি controller এ try-catch নয়; এক জায়গায় ধরুন, লগ করুন, ক্লায়েন্টকে নিরাপদ বার্তা দিন (stack trace কখনো নয়)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৫ Pagination: বিশাল তালিকা সামলানো
&lt;/h3&gt;

&lt;p&gt;১০ লক্ষ পণ্য একসাথে ফেরত দেওয়া যায় না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offset Pagination:&lt;/strong&gt; &lt;code&gt;GET /products?page=3&amp;amp;pageSize=20&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;সহজ, কিন্তু বড় পৃষ্ঠা নম্বরে ধীর (&lt;code&gt;OFFSET 1000000&lt;/code&gt; মানে ১০ লক্ষ row পেরিয়ে যেতে হয়) ও পড়ার মাঝে ডেটা বদলালে row ডুপ্লিকেট/হারায়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keyset (Cursor) Pagination:&lt;/strong&gt; &lt;code&gt;GET /products?after=12345&amp;amp;limit=20&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;afterId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Index ব্যবহার করে সবসময় দ্রুত, infinite scroll এর জন্য আদর্শ। দাম: নির্দিষ্ট পৃষ্ঠায় লাফ দেওয়া যায় না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; সবসময় একটা সর্বোচ্চ &lt;code&gt;pageSize&lt;/code&gt; সীমা রাখুন (যেমন ১০০)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৬ Filtering, Sorting ও Search
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET /products?category=phone&amp;amp;minPrice=10000&amp;amp;sort=-price&lt;/code&gt; (&lt;code&gt;-&lt;/code&gt; মানে descending)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;সতর্কতা:&lt;/strong&gt; sort/filter এর ফিল্ড নাম ইউজারের কাছ থেকে নিয়ে সরাসরি query তে বসাবেন না; অনুমোদিত তালিকা (allow-list) রাখুন। বিশাল full-text search এর জন্য database নয়, Elasticsearch/Azure AI Search জাতীয় ব্যবহার করুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৭ API Versioning
&lt;/h3&gt;

&lt;p&gt;আজ আপনার API কাজ করে। কাল আপনি ফিল্ড বদলালেন, পুরনো মোবাইল অ্যাপ ভাঙল! ভার্সন দিয়ে বাঁচুন।&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;পদ্ধতি&lt;/th&gt;
&lt;th&gt;উদাহরণ&lt;/th&gt;
&lt;th&gt;মন্তব্য&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;সবচেয়ে স্পষ্ট ও জনপ্রিয়&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/orders?api-version=1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;সহজ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header&lt;/td&gt;
&lt;td&gt;&lt;code&gt;X-Api-Version: 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL পরিষ্কার, কম আবিষ্কারযোগ্য&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;.NET এ &lt;code&gt;Asp.Versioning.Http&lt;/code&gt; প্যাকেজ।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ভাঙা-না-ভাঙা পরিবর্তন:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ নিরাপদ: নতুন ঐচ্ছিক ফিল্ড/endpoint যোগ&lt;/li&gt;
&lt;li&gt;❌ ভাঙে: ফিল্ড মুছা/নাম বদল, type বদল, নতুন বাধ্যতামূলক ইনপুট&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;পুরনো ভার্সন কবে বন্ধ হবে (&lt;strong&gt;Deprecation&lt;/strong&gt; নীতি) আগে জানান।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৮ Idempotency: পেমেন্ট দুবার কাটা ঠেকানো
&lt;/h3&gt;

&lt;p&gt;ShopEasy এর সেই সমস্যা: ইউজার "Pay" চাপল, নেটওয়ার্ক ধীর, response ফেরেনি, অ্যাপ আবার চেষ্টা করল। দুবার টাকা কাটল।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;সমাধান: Idempotency Key।&lt;/strong&gt; ক্লায়েন্ট প্রতিটি আসল কাজের জন্য একটা অনন্য key পাঠায়। সার্ভার একই key দ্বিতীয়বার পেলে নতুন কাজ না করে আগের ফলাফল ফেরত দেয়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Idempotency-Key"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PaymentRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IPaymentGateway&lt;/span&gt; &lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdempotencyRecords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ChargeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdempotencyRecords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// key এর ওপর unique constraint রাখুন&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/payments/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(প্রোডাকশনে "processing" অবস্থার জন্য লক/অবস্থা-ট্র্যাকিং দরকার, নইলে একই key এ দুটো সমান্তরাল request একসাথে ঢুকতে পারে। key এর ওপর unique constraint এ এটা সামলানো যায়।)&lt;/p&gt;

&lt;p&gt;GET/PUT/DELETE প্রকৃতিগতভাবে idempotent; &lt;strong&gt;POST এর জন্য এটা আপনাকে বানাতে হবে।&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.৯ Rate Limiting ও Throttling
&lt;/h3&gt;

&lt;p&gt;এক ক্লায়েন্ট বা বট সবার সার্ভার দখল করতে পারে। রক্ষা:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RejectionStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;429&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFixedWindowLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRateLimiter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;...).&lt;/span&gt;&lt;span class="nf"&gt;RequireRateLimiting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;অ্যালগরিদম:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fixed Window:&lt;/strong&gt; প্রতি মিনিটে ১০০। সহজ; জানালার সীমানায় দ্বিগুণ burst সম্ভব&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sliding Window:&lt;/strong&gt; আরও মসৃণ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Bucket:&lt;/strong&gt; বালতিতে টোকেন জমে, প্রতি request এ একটা খরচ; ছোট burst অনুমোদিত, গড় হার নিয়ন্ত্রিত&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency Limiter:&lt;/strong&gt; একসাথে সর্বোচ্চ N টা চলমান request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;একাধিক server থাকলে counter শেয়ার করতে Redis লাগে। &lt;code&gt;Retry-After&lt;/code&gt; header দিন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.১০ gRPC, GraphQL ও REST: কখন কোনটা
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;REST&lt;/th&gt;
&lt;th&gt;gRPC&lt;/th&gt;
&lt;th&gt;GraphQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;প্রোটোকল&lt;/td&gt;
&lt;td&gt;HTTP/JSON&lt;/td&gt;
&lt;td&gt;HTTP/2 + Protobuf (binary)&lt;/td&gt;
&lt;td&gt;HTTP + JSON, একটাই endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;গতি&lt;/td&gt;
&lt;td&gt;ভালো&lt;/td&gt;
&lt;td&gt;সর্বোচ্চ&lt;/td&gt;
&lt;td&gt;ভালো&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ব্রাউজার সাপোর্ট&lt;/td&gt;
&lt;td&gt;সরাসরি&lt;/td&gt;
&lt;td&gt;সীমিত (gRPC-Web লাগে)&lt;/td&gt;
&lt;td&gt;সরাসরি&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract&lt;/td&gt;
&lt;td&gt;OpenAPI&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.proto&lt;/code&gt; ফাইল (কঠোর)&lt;/td&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;সীমিত&lt;/td&gt;
&lt;td&gt;দ্বিমুখী streaming&lt;/td&gt;
&lt;td&gt;Subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;কখন&lt;/td&gt;
&lt;td&gt;পাবলিক API, সাধারণ&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;ভেতরের microservice যোগাযোগ&lt;/strong&gt;, কম latency&lt;/td&gt;
&lt;td&gt;বিভিন্ন ক্লায়েন্ট, নমনীয় ডেটা চাহিদা&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;GraphQL এ ক্লায়েন্ট ঠিক করে কী কী ডেটা চাই&lt;/strong&gt; (over/under-fetching কমে)। তবে caching, N+1, query জটিলতা সীমিত করা ও নিরাপত্তা সামলাতে বাড়তি যত্ন লাগে। .NET এ HotChocolate জনপ্রিয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gRPC উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;Inventory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;CheckStock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StockRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StockReply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;StockRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;StockReply&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৫.১১ HTTP Caching ও Conditional Request
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Cache-Control: public, max-age=300&lt;/code&gt; ব্রাউজার/CDN কে ৫ মিনিট রাখতে বলে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ETag:&lt;/strong&gt; response এর "আঙুলের ছাপ"। ক্লায়েন্ট পরেরবার &lt;code&gt;If-None-Match: "abc"&lt;/code&gt; পাঠায়; ডেটা না বদলালে সার্ভার &lt;code&gt;304 Not Modified&lt;/code&gt; (বডি ছাড়া) দেয়, ব্যান্ডউইথ বাঁচে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic concurrency ও ETag/&lt;code&gt;If-Match&lt;/code&gt;:&lt;/strong&gt; আপডেটের সময় পাঠানো ETag না মিললে &lt;code&gt;412 Precondition Failed&lt;/code&gt;, ফলে একজনের বদল অন্যজন অজান্তে মুছে দেয় না।&lt;/li&gt;
&lt;li&gt;Output Caching (&lt;code&gt;app.UseOutputCache()&lt;/code&gt;) সার্ভার-পাশে response cache করে।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৫.১২ API Gateway ও BFF
&lt;/h3&gt;

&lt;p&gt;মাইক্রোসার্ভিসে ক্লায়েন্ট ১০টা সার্ভিসকে সরাসরি ডাকবে না। মাঝে &lt;strong&gt;API Gateway&lt;/strong&gt; একটা একক প্রবেশপথ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing, Authentication, Rate limiting, SSL termination, Logging এক জায়গায়&lt;/li&gt;
&lt;li&gt;Request aggregation (একাধিক সার্ভিস থেকে ডেটা জুড়ে একটা response)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;.NET এ &lt;strong&gt;YARP&lt;/strong&gt; (Yet Another Reverse Proxy, Microsoft এর), Ocelot; ক্লাউডে Azure API Management, AWS API Gateway, Kong।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BFF (Backend for Frontend):&lt;/strong&gt; মোবাইল ও ওয়েবের জন্য আলাদা gateway, যাতে প্রতিটি ক্লায়েন্ট ঠিক যা চায় তা পায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.১৩ API ডকুমেন্টেশন
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI/Swagger&lt;/strong&gt; দিয়ে স্বয়ংক্রিয় ডকুমেন্টেশন। .NET 9 এ built-in &lt;code&gt;Microsoft.AspNetCore.OpenApi&lt;/code&gt; আছে; UI এর জন্য Scalar বা Swagger UI যুক্ত করা যায়। ভালো API ডকুমেন্টে উদাহরণ ও error কোড থাকে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৫.১৪ Async কাজ ও Webhook
&lt;/h3&gt;

&lt;p&gt;দীর্ঘ কাজ (রিপোর্ট তৈরি, ভিডিও প্রসেসিং) HTTP request এ আটকে রাখবেন না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async Request-Reply:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;POST /reports&lt;/code&gt; → &lt;code&gt;202 Accepted&lt;/code&gt; + &lt;code&gt;Location: /reports/job-77&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ক্লায়েন্ট &lt;code&gt;GET /reports/job-77&lt;/code&gt; poll করে → &lt;code&gt;status: processing&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;শেষে &lt;code&gt;status: completed, url: ...&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Webhook:&lt;/strong&gt; সার্ভার নিজেই ক্লায়েন্টের URL এ খবর পাঠায় (যেমন পেমেন্ট সফল হলে gateway আপনার সার্ভারে POST করে)। যাচাই করুন (signature), retry সামলান, idempotent রাখুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 5 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Resource-ভিত্তিক URL, সঠিক method ও status code।&lt;/li&gt;
&lt;li&gt;DTO ব্যবহার করুন; entity বাইরে নয়।&lt;/li&gt;
&lt;li&gt;Pagination বাধ্যতামূলক; বড় ডেটায় keyset।&lt;/li&gt;
&lt;li&gt;POST এর জন্য Idempotency Key, রক্ষায় Rate Limiting।&lt;/li&gt;
&lt;li&gt;Public API তে REST, ভেতরে gRPC, নমনীয়তায় GraphQL; Gateway দিয়ে প্রবেশপথ এক।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 6: Caching, Messaging ও Asynchronous Processing
&lt;/h2&gt;

&lt;p&gt;ShopEasy এর ঈদের সেলের দিন হোমপেজ খুলতে ১০ সেকেন্ড লাগছে, কারণ প্রতিটি visitor এ একই "জনপ্রিয় পণ্য" query ডেটাবেসে যাচ্ছে। আবার অর্ডার করার সময় ইমেইল পাঠাতে গিয়ে ইউজারকে ৫ সেকেন্ড অপেক্ষা করতে হচ্ছে। এই Part এই দুই সমস্যার সমাধান: &lt;strong&gt;Cache&lt;/strong&gt; (একই কাজ বারবার না করা) ও &lt;strong&gt;Messaging&lt;/strong&gt; (ধীর কাজ পেছনে পাঠানো)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.১ Caching: সবচেয়ে শক্তিশালী পারফরম্যান্স অস্ত্র
&lt;/h3&gt;

&lt;p&gt;উপমা: বারবার লাইব্রেরি গিয়ে একই বই না খুলে প্রয়োজনীয় পাতার ফটোকপি টেবিলে রাখা।&lt;/p&gt;

&lt;p&gt;মনে করুন, মেমরি থেকে পড়া ~100 ns, আর DB query ~5 ms (৫০,০০০ গুণ ধীর)। বহু ব্যবহারকারী একই ডেটা চাইলে cache বিরাট জয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache কোথায় বসে (স্তর):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Browser cache:&lt;/strong&gt; ক্লায়েন্টের নিজের কাছে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN:&lt;/strong&gt; ইউজারের কাছের edge সার্ভারে&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Gateway/Reverse proxy cache&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application in-memory cache:&lt;/strong&gt; অ্যাপের নিজের মেমরিতে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed cache (Redis):&lt;/strong&gt; একাধিক সার্ভারের শেয়ার করা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database cache:&lt;/strong&gt; DB এর নিজস্ব buffer&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ৬.২ .NET এ Cache এর ধরন
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;১. In-Memory Cache (&lt;code&gt;IMemoryCache&lt;/code&gt;):&lt;/strong&gt; সবচেয়ে দ্রুত, কিন্তু প্রতিটি server এর নিজস্ব; একাধিক server এ আলাদা কপি, অসঙ্গত হতে পারে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetPopularAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"popular"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlidingExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderByDescending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SalesCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProductDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;২. Distributed Cache (&lt;code&gt;IDistributedCache&lt;/code&gt; + Redis):&lt;/strong&gt; সব সার্ভার একই cache দেখে। এটা network call (~১ ms), তাই memory এর চেয়ে ধীর, কিন্তু DB এর চেয়ে বহু দ্রুত।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddStackExchangeRedisCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;৩. HybridCache (.NET 9+):&lt;/strong&gt; দুটো স্তর একসাথে (L1 in-memory + L2 Redis) এবং &lt;strong&gt;cache stampede protection&lt;/strong&gt; সহ। নতুন কোডে এটাই সবচেয়ে সুবিধাজনক।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHybridCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HybridCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductDto&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"product:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;LoadFromDbAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;৪. Output Caching ও Response Caching:&lt;/strong&gt; পুরো HTTP response cache।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.৩ Caching এর কৌশল (Pattern)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;কীভাবে কাজ করে&lt;/th&gt;
&lt;th&gt;কখন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache-Aside (Lazy Loading)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;অ্যাপ আগে cache দেখে; না পেলে DB থেকে এনে cache এ রাখে&lt;/td&gt;
&lt;td&gt;সবচেয়ে সাধারণ, সবচেয়ে নিরাপদ শুরু&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read-Through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;cache নিজেই DB থেকে আনে&lt;/td&gt;
&lt;td&gt;cache লাইব্রেরি সমর্থন করলে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-Through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;লেখার সময় cache ও DB দুটোতেই&lt;/td&gt;
&lt;td&gt;cache সবসময় সতেজ চাই, লেখা ধীর&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-Behind (Write-Back)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;আগে cache এ, পরে async DB তে&lt;/td&gt;
&lt;td&gt;লেখা অনেক, ডেটা হারানোর ঝুঁকি সহ্য্য&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Refresh-Ahead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;মেয়াদ শেষের আগেই আগাম নবায়ন&lt;/td&gt;
&lt;td&gt;পূর্বানুমেয় হট ডেটা&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cache-Aside কোড:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"product:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// cache miss&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DistributedCacheEntryOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৬.৪ Cache Invalidation: কম্পিউটার বিজ্ঞানের কঠিন সমস্যা
&lt;/h3&gt;

&lt;p&gt;প্রবাদ: "Computer science এ দুটো কঠিন জিনিস: cache invalidation আর নামকরণ।" কারণ cache এ থাকা ডেটা পুরনো (stale) হয়ে যেতে পারে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কৌশল:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTL (Time-To-Live):&lt;/strong&gt; নির্দিষ্ট সময় পর মেয়াদ শেষ। সবচেয়ে সহজ; সামান্য পুরনো ডেটা সহনীয় হলে চমৎকার।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;সক্রিয় invalidate:&lt;/strong&gt; ডেটা বদলালে cache মুছে দেওয়া (&lt;code&gt;cache.Remove(key)&lt;/code&gt;)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-based:&lt;/strong&gt; ডেটা বদলের event শুনে বিভিন্ন সার্ভারের cache মোছা।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned key:&lt;/strong&gt; &lt;code&gt;product:5:v12&lt;/code&gt; (সংস্করণ বদলালে নতুন key)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;সাধারণ সমস্যা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache Stampede (Thundering Herd):&lt;/strong&gt; জনপ্রিয় key র মেয়াদ শেষ; এক মুহূর্তে হাজারো request সবাই DB তে যায়। সমাধান: লক/single-flight (HybridCache স্বয়ংক্রিয় করে), TTL তে র‍্যান্ডম জিটার, আগাম নবায়ন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Penetration:&lt;/strong&gt; এমন key র বারবার অনুরোধ যা DB তেও নেই (যেমন &lt;code&gt;/products/-1&lt;/code&gt;); প্রতিবার DB hit। সমাধান: "পাওয়া যায়নি" ফলাফলও সংক্ষিপ্ত সময়ের জন্য cache, বা Bloom Filter।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Avalanche:&lt;/strong&gt; একসাথে অনেক key র মেয়াদ শেষ। সমাধান: TTL এ জিটার।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot Key:&lt;/strong&gt; একটা key এ বিপুল চাপ। সমাধান: local L1 cache, key প্রতিলিপি।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Eviction Policy:&lt;/strong&gt; মেমরি ভরলে কী ফেলা হবে: &lt;strong&gt;LRU&lt;/strong&gt; (সবচেয়ে কম সম্প্রতি ব্যবহৃত), LFU (সবচেয়ে কম ঘনঘন), FIFO।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কী cache করবেন, কী নয়:&lt;/strong&gt; ঘনঘন পড়া, কম বদলানো, তৈরি করা ব্যয়বহুল ডেটা (পণ্য তালিকা, কনফিগ, রেফারেন্স ডেটা)। ব্যবহারকারী-নির্দিষ্ট সংবেদনশীল ডেটা (ভুল key তে অন্যের তথ্য দেখানোর ঝুঁকি) ও দ্রুত বদলানো ডেটা (শেষ স্টক সংখ্যা) সতর্কভাবে। &lt;strong&gt;কখনো প্রমাণ ছাড়া cache যোগ করবেন না&lt;/strong&gt;; আগে মাপুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.৫ Redis: শুধু cache নয়
&lt;/h3&gt;

&lt;p&gt;Redis একটা in-memory data structure store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String:&lt;/strong&gt; সাধারণ cache, counter (&lt;code&gt;INCR&lt;/code&gt; atomic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash:&lt;/strong&gt; অবজেক্ট (যেমন user session)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List:&lt;/strong&gt; queue, সাম্প্রতিক কার্যকলাপ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set / Sorted Set:&lt;/strong&gt; unique তালিকা, &lt;strong&gt;leaderboard&lt;/strong&gt;, rate limiting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pub/Sub, Streams:&lt;/strong&gt; বার্তা আদান-প্রদান&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Lock:&lt;/strong&gt; একাধিক সার্ভারে একই কাজ একবারই চালাতে&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;সতর্কতা: Redis মূলত মেমরিতে; persistence (RDB/AOF) থাকলেও এটাকে একমাত্র "source of truth" বানাবেন না, আর সংযোগ ব্যর্থ হলে অ্যাপ যেন DB তে fallback করতে পারে (graceful degradation)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.৬ Message Queue: ধীর কাজ পেছনে পাঠানো
&lt;/h3&gt;

&lt;p&gt;অর্ডার দিলে: বিলিং, ইনভয়েস PDF, ইমেইল, SMS, স্টক আপডেট। সব একসাথে করলে ইউজার ১০ সেকেন্ড অপেক্ষা করে, আর ইমেইল সার্ভার ডাউন থাকলে পুরো অর্ডারই ব্যর্থ!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;সমাধান:&lt;/strong&gt; ইউজারকে বলুন "অর্ডার গৃহীত"। বাকি কাজের জন্য &lt;strong&gt;বার্তা (message)&lt;/strong&gt; queue তে রাখুন; আলাদা &lt;strong&gt;worker&lt;/strong&gt; পরে সেগুলো করবে।&lt;/p&gt;

&lt;p&gt;উপমা: রেস্টুরেন্টে ক্যাশিয়ার অর্ডার নিয়ে টিকিট রান্নাঘরে ঝুলিয়ে দেয়। ক্যাশিয়ার রান্নার জন্য অপেক্ষা করে না, পরের গ্রাহক নেয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;শব্দাবলি:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Producer:&lt;/strong&gt; বার্তা পাঠায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue/Topic:&lt;/strong&gt; বার্তা জমা থাকার জায়গা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumer:&lt;/strong&gt; বার্তা নিয়ে কাজ করে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broker:&lt;/strong&gt; পুরো ব্যবস্থার ম্যানেজার (RabbitMQ, Kafka, Azure Service Bus)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;সুবিধা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; প্রেরক জানেই না কে নেবে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load leveling (Buffer):&lt;/strong&gt; হঠাৎ ট্রাফিক-ঢেউ queue শোষে নেয়, consumer নিজের গতিতে সামলায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience:&lt;/strong&gt; consumer ডাউন থাকলেও বার্তা হারায় না&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; consumer সংখ্যা বাড়ালেই দ্রুত&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৬.৭ Queue বনাম Pub/Sub বনাম Stream
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Point-to-Point Queue:&lt;/strong&gt; প্রতিটি বার্তা &lt;strong&gt;একজন&lt;/strong&gt; consumer পায় (কাজ ভাগ করা: Competing Consumers)। উদাহরণ: ১০০০ ইমেইল ১০ জন worker ভাগ করে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish/Subscribe (Topic):&lt;/strong&gt; প্রতিটি বার্তা &lt;strong&gt;সব&lt;/strong&gt; subscriber পায়। &lt;code&gt;OrderPlaced&lt;/code&gt; → Inventory, Email, Analytics সবাই।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log/Stream (Kafka):&lt;/strong&gt; বার্তা মুছে যায় না, ক্রমানুসারে লগে থাকে; consumer নিজের অবস্থান (offset) মনে রেখে পড়ে, চাইলে পুরনোটা আবার পড়তে পারে (replay)।&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;RabbitMQ&lt;/th&gt;
&lt;th&gt;Kafka&lt;/th&gt;
&lt;th&gt;Azure Service Bus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;মডেল&lt;/td&gt;
&lt;td&gt;Smart broker, queue/exchange&lt;/td&gt;
&lt;td&gt;Distributed log&lt;/td&gt;
&lt;td&gt;Managed enterprise broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;গতি/ধারণক্ষমতা&lt;/td&gt;
&lt;td&gt;ভালো&lt;/td&gt;
&lt;td&gt;অসাধারণ (লক্ষ/সেকেন্ড)&lt;/td&gt;
&lt;td&gt;ভালো&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replay&lt;/td&gt;
&lt;td&gt;সাধারণত না&lt;/td&gt;
&lt;td&gt;হ্যাঁ&lt;/td&gt;
&lt;td&gt;সীমিত&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ক্রম (Ordering)&lt;/td&gt;
&lt;td&gt;queue তে&lt;/td&gt;
&lt;td&gt;partition এর ভেতরে&lt;/td&gt;
&lt;td&gt;session দিয়ে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;কখন&lt;/td&gt;
&lt;td&gt;সাধারণ task queue, routing&lt;/td&gt;
&lt;td&gt;ইভেন্ট স্ট্রিম, বিগ ডেটা, analytics&lt;/td&gt;
&lt;td&gt;Azure এ, নির্ভরযোগ্য enterprise messaging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;.NET এ সহজ করার লাইব্রেরি: &lt;strong&gt;MassTransit&lt;/strong&gt;, &lt;strong&gt;Wolverine&lt;/strong&gt;, NServiceBus, বা সরাসরি client SDK। (বাণিজ্যিক লাইসেন্স নিয়ম বদলাতে পারে, ব্যবহারের আগে দেখে নিন।)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MassTransit উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Event&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;CustomerEmail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Publish&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;publishEndpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Consumer&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConsumeContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"Order &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; confirmed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Registration&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMassTransit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SendConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsingRabbitMq&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rabbitmq://localhost"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৬.৮ Messaging এর কঠিন সমস্যাগুলো (এখানেই পেশাদারিত্ব)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ১. Delivery Guarantee
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At-most-once:&lt;/strong&gt; সর্বোচ্চ একবার, হারাতে পারে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At-least-once:&lt;/strong&gt; অন্তত একবার, &lt;strong&gt;ডুপ্লিকেট হতে পারে&lt;/strong&gt; (সবচেয়ে সাধারণ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exactly-once:&lt;/strong&gt; বাস্তবে প্রায় অসম্ভব, ব্যয়বহুল। কার্যকর সমাধান: At-least-once + &lt;strong&gt;Idempotent Consumer&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ২. Idempotent Consumer
&lt;/h4&gt;

&lt;p&gt;বার্তা দুবার এলে প্রভাব একবারের সমান হতে হবে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConsumeContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessedMessages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AnyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// আগেই করা হয়েছে&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;DoWorkAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessedMessages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessedMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// কাজ + রেকর্ড একই transaction এ&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ৩. Retry ও Dead Letter Queue (DLQ)
&lt;/h4&gt;

&lt;p&gt;প্রক্রিয়া ব্যর্থ হলে: কয়েকবার retry (exponential backoff সহ: ১s, ২s, ৪s...)। তবু ব্যর্থ হলে &lt;strong&gt;Dead Letter Queue&lt;/strong&gt; এ রাখুন, যাতে একটা "বিষাক্ত বার্তা" (poison message) পুরো queue আটকে না দেয়। DLQ নিয়মিত পর্যবেক্ষণ করুন ও alert দিন।&lt;/p&gt;

&lt;h4&gt;
  
  
  ৪. Message Ordering
&lt;/h4&gt;

&lt;p&gt;ক্রম সবসময় নিশ্চিত নয়। যদি ক্রম জরুরি (যেমন একই অর্ডারের Created → Paid → Shipped), Kafka তে একই key (OrderId) কে একই partition এ পাঠান; অথবা consumer ক্রম-নিরপেক্ষ ডিজাইন করুন (version/timestamp দেখে পুরনো বার্তা বাদ)।&lt;/p&gt;

&lt;h4&gt;
  
  
  ৫. Outbox Pattern: ডেটাবেস ও বার্তার সামঞ্জস্য
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; অর্ডার DB তে সংরক্ষণ হলো কিন্তু ঠিক তখন অ্যাপ ক্র্যাশ করল, বার্তা পাঠানো হলো না। বা উল্টো: বার্তা গেল কিন্তু DB তে সেভ ব্যর্থ। কারণ DB ও broker আলাদা সিস্টেম, এক transaction এ আনা যায় না (&lt;strong&gt;Dual Write সমস্যা&lt;/strong&gt;)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;সমাধান: Transactional Outbox।&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;অর্ডার সংরক্ষণের &lt;strong&gt;একই transaction এ&lt;/strong&gt; &lt;code&gt;Outbox&lt;/code&gt; table এ বার্তাও লিখুন।&lt;/li&gt;
&lt;li&gt;আলাদা একটি background process Outbox পড়ে broker এ পাঠায়, সফল হলে "পাঠানো হয়েছে" চিহ্নিত করে।&lt;/li&gt;
&lt;li&gt;At-least-once ফলে consumer idempotent রাখুন।
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransactionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutboxMessages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OutboxMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// অর্ডার ও বার্তা: হয় দুটোই, নয় কোনোটাই&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MassTransit এ built-in Outbox সমর্থন আছে। &lt;strong&gt;Inbox Pattern&lt;/strong&gt; হলো consumer-পাশের অনুরূপ (ডুপ্লিকেট প্রতিরোধ)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.৯ Saga Pattern: Distributed Transaction সামলানো
&lt;/h3&gt;

&lt;p&gt;Monolith এ একটা DB transaction সব ঠিক রাখে। Microservices এ &lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;Payment&lt;/code&gt;, &lt;code&gt;Inventory&lt;/code&gt; আলাদা DB তে। একটা ধাপ ফেল করলে আগের ধাপগুলো কীভাবে ফেরত নেবেন? Two-Phase Commit (2PC) ধীর ও scalable নয়। সমাধান: &lt;strong&gt;Saga&lt;/strong&gt;, একাধিক local transaction এর ধারা, ব্যর্থ হলে &lt;strong&gt;Compensating Action&lt;/strong&gt; (উল্টো ক্ষতিপূরণ কাজ)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ShopEasy অর্ডার Saga:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Order তৈরি (Pending)&lt;/li&gt;
&lt;li&gt;Payment নেওয়া&lt;/li&gt;
&lt;li&gt;Inventory সংরক্ষণ&lt;/li&gt;
&lt;li&gt;Shipping শুরু → Order Confirmed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;যদি ধাপ ৩ ব্যর্থ (স্টক শেষ):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment &lt;strong&gt;ফেরত (Refund)&lt;/strong&gt;  ← compensating action&lt;/li&gt;
&lt;li&gt;Order &lt;strong&gt;বাতিল (Cancelled)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;দুই ধরন:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choreography:&lt;/strong&gt; কেন্দ্রীয় নিয়ন্ত্রক নেই; প্রতিটি সার্ভিস event শোনে ও পরের event ছাড়ে। সরল, কিন্তু ধাপ বাড়লে প্রবাহ বোঝা কঠিন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration:&lt;/strong&gt; একটা কেন্দ্রীয় "Saga orchestrator" সবাইকে নির্দেশ দেয় (MassTransit State Machine, Temporal, Durable Functions)। প্রবাহ স্পষ্ট, কিন্তু orchestrator একটি নির্ভরতার কেন্দ্র।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;মনে রাখুন: compensation সবসময় নিখুঁত "undo" নয়। ইমেইল পাঠানো ফেরানো যায় না; একটা "ভুলের জন্য দুঃখিত" ইমেইলই ক্ষতিপূরণ। এভাবে চিন্তা করতে হবে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.১০ Background Job ও Scheduled Task
&lt;/h3&gt;

&lt;p&gt;সব কাজ বার্তার প্রয়োজন নেই; কিছু কাজ নির্ধারিত সময়ে বা পেছনে চলে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET এর built-in &lt;code&gt;BackgroundService&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StaleCartCleaner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StaleCartCleaner&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PeriodicTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForNextTickAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scopeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// Scoped service এর জন্য&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Carts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteDeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cleanup failed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// loop যেন না মরে&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StaleCartCleaner&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সতর্কতা:&lt;/strong&gt; অ্যাপের একাধিক instance চললে প্রতিটি এই কাজ চালাবে! একবার চালাতে চাইলে distributed lock বা Hangfire/Quartz.NET এর মতো লাইব্রেরি (যা persistent job store ও cluster-aware) ব্যবহার করুন।&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;টুল&lt;/th&gt;
&lt;th&gt;কখন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BackgroundService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;সহজ, একক-instance বা idempotent কাজ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hangfire&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;persistent job, retry, ড্যাশবোর্ড, fire-and-forget/delayed/recurring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quartz.NET&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;জটিল schedule (cron)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Functions Timer&lt;/td&gt;
&lt;td&gt;serverless schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Channel (&lt;code&gt;System.Threading.Channels&lt;/code&gt;):&lt;/strong&gt; একই process এর ভেতরে producer-consumer queue (বাইরের broker ছাড়া হালকা)। তবে process ক্র্যাশ করলে বার্তা হারায়; গুরুত্বপূর্ণ কাজে durable broker নিন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৬.১১ ShopEasy তে প্রয়োগ
&lt;/h3&gt;

&lt;p&gt;রাফি এখন:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;হোমপেজের জনপ্রিয় পণ্য: &lt;strong&gt;HybridCache&lt;/strong&gt; (TTL ৫ মিনিট)&lt;/li&gt;
&lt;li&gt;পণ্য ছবি: &lt;strong&gt;CDN&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;অর্ডারের পর ইমেইল/SMS/ইনভয়েস: &lt;strong&gt;RabbitMQ + MassTransit + Outbox&lt;/strong&gt;, consumer গুলো idempotent&lt;/li&gt;
&lt;li&gt;প্রতি রাতের রিপোর্ট: &lt;strong&gt;Hangfire&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Order → Payment → Inventory: &lt;strong&gt;Saga (orchestration)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ফল: ঈদের সেলে হোমপেজ ২০০ ms, অর্ডার ১ সেকেন্ডে গৃহীত, ইমেইল সার্ভার ডাউন থাকলেও কোনো অর্ডার ব্যর্থ হয় না।&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 6 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cache যোগ করার আগে মাপুন; Cache-Aside দিয়ে শুরু; HybridCache আধুনিক পছন্দ।&lt;/li&gt;
&lt;li&gt;Invalidation সবচেয়ে কঠিন; TTL + stampede প্রতিরোধ মাথায় রাখুন।&lt;/li&gt;
&lt;li&gt;ধীর ও অ-জরুরি কাজ queue তে; broker ও ডেটাবেসের সামঞ্জস্যে Outbox।&lt;/li&gt;
&lt;li&gt;At-least-once + Idempotent Consumer + Retry + DLQ হলো messaging এর চার স্তম্ভ।&lt;/li&gt;
&lt;li&gt;Distributed transaction এর জন্য Saga; compensation ভাবুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 7: Distributed System ও Resilience: ব্যর্থতা মেনেই বাঁচা
&lt;/h2&gt;

&lt;p&gt;মাইক্রোসার্ভিসে ShopEasy তে একটা অর্ডার এখন ৫টা সার্ভিস ডাকে। একটা সার্ভিস ধীর হলো (Payment gateway ৩০ সেকেন্ড নিচ্ছে)। ফল: Order সার্ভিসের সব thread আটকে গেল, তারপর API Gateway, তারপর পুরো সাইট ডাউন। একটা ছোট সমস্যা ঝর্ণার মতো সবখানে ছড়াল। এটাকে বলে &lt;strong&gt;Cascading Failure&lt;/strong&gt;। এই Part এ শিখব কীভাবে এমন না হয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;মূলমন্ত্র:&lt;/strong&gt; "Everything fails, all the time" (ওয়ার্নার ভোগেলস, Amazon এর CTO)। ব্যর্থতা এড়ানো যায় না, তাই ব্যর্থতা মেনে নিয়ে নকশা করতে হয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.১ Timeout: সবচেয়ে মৌলিক সুরক্ষা
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ডিফল্ট &lt;code&gt;HttpClient&lt;/code&gt; timeout ১০০ সেকেন্ড।&lt;/strong&gt; এটা প্রায় সবসময় অনেক বেশি। কোনো নেটওয়ার্ক call এ timeout না থাকলে একটা ধীর নির্ভরতা আপনার সব thread খেয়ে ফেলতে পারে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://payment.internal"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// সীমা ঠিক করুন&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; প্রতিটি বাইরের call (HTTP, DB, cache, queue) এ timeout দিন, এবং ভেতরের timeout বাইরের চেয়ে ছোট রাখুন। (ক্লায়েন্ট ৫s অপেক্ষা করে, কিন্তু সার্ভার ১০s ধরে কাজ করছে, এটা অপচয়।)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deadline propagation:&lt;/strong&gt; সম্পূর্ণ request এর একটা মোট সময়সীমা ধরে &lt;code&gt;CancellationToken&lt;/code&gt; সবখানে পাস করুন। ক্লায়েন্ট চলে গেলে বা সময় শেষ হলে পুরো শৃঙ্খলে কাজ বন্ধ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.২ Retry: আবার চেষ্টা, কিন্তু বুদ্ধি করে
&lt;/h3&gt;

&lt;p&gt;অনেক ব্যর্থতা ক্ষণস্থায়ী (transient): নেটওয়ার্ক ঝলক, ৫০৩, deadlock। আবার চেষ্টায় কাজ হয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কিন্তু অন্ধ retry বিপজ্জনক:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry Storm:&lt;/strong&gt; সার্ভার ধুঁকছে; ১০০০ ক্লায়েন্ট একসাথে retry করে তাকে শেষ করে দিল।&lt;/li&gt;
&lt;li&gt;অ-idempotent কাজ (POST payment) retry করলে ডুপ্লিকেট (Part 5 এর Idempotency Key)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ভালো retry এর উপাদান:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;সীমিত সংখ্যক&lt;/strong&gt; (২-৪ বার)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential Backoff:&lt;/strong&gt; ১s → ২s → ৪s (মাঝের ফাঁক বাড়ানো)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jitter:&lt;/strong&gt; র‍্যান্ডম সময় যোগ, যাতে সবাই একসাথে না আসে&lt;/li&gt;
&lt;li&gt;শুধু &lt;strong&gt;retry-যোগ্য&lt;/strong&gt; ব্যর্থতায় (৫০৩, timeout, ৪২৯); ৪০০/৪০৪ এ নয়&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ৭.৩ Circuit Breaker: ফিউজ
&lt;/h3&gt;

&lt;p&gt;বাড়ির ফিউজের মতো: সমস্যা ধরা পড়লে সংযোগ কেটে দেয়, যাতে বড় বিপদ না হয়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;তিন অবস্থা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Closed (স্বাভাবিক):&lt;/strong&gt; request যায়। ব্যর্থতা গোনা হয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open (কাটা):&lt;/strong&gt; ব্যর্থতা সীমা ছাড়ালে বন্ধ। request &lt;strong&gt;তৎক্ষণাৎ&lt;/strong&gt; ব্যর্থ (অপেক্ষা ছাড়াই), নির্ভরতাকে সুস্থ হওয়ার সময় দেয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half-Open (পরীক্ষা):&lt;/strong&gt; কিছু সময় পর অল্প কয়েকটা request ছেড়ে দেখা। সফল হলে Closed, নইলে আবার Open।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ফল: Payment gateway ডাউন থাকলে প্রতিটি অর্ডার ৩০s আটকে না থেকে সাথে সাথে ব্যর্থ হয় (বা বিকল্প পথে যায়), আমাদের সার্ভার সুস্থ থাকে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.৪ Polly ও Microsoft.Extensions.Resilience
&lt;/h3&gt;

&lt;p&gt;.NET এ resilience এর জনপ্রিয় লাইব্রেরি &lt;strong&gt;Polly&lt;/strong&gt;। .NET 8+ এ এটি &lt;code&gt;Microsoft.Extensions.Http.Resilience&lt;/code&gt; এ সহজে &lt;code&gt;HttpClient&lt;/code&gt; এর সাথে মেলানো যায়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://payment.internal"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddStandardResilienceHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// ডিফল্ট: rate limiter, total timeout, retry, circuit breaker, attempt timeout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;নিজের মতো কাস্টমাইজ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddResilienceHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"payment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HttpRetryStrategyOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MaxRetryAttempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BackoffType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DelayBackoffType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exponential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;UseJitter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Delay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCircuitBreaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HttpCircuitBreakerStrategyOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FailureRatio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MinimumThroughput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SamplingDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;BreakDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Polly v8 এর Resilience Pipeline API এখানে দেখানো। পুরনো &lt;code&gt;Policy.Handle&amp;lt;...&amp;gt;()&lt;/code&gt; শৈলী v7 এর।)&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.৫ Bulkhead, Fallback ও Graceful Degradation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bulkhead (জাহাজের বদ্ধ কুঠুরি):&lt;/strong&gt; একটা কুঠুরিতে পানি ঢুকলেও বাকি জাহাজ ভাসে। কোডে: প্রতিটি নির্ভরতার জন্য আলাদা resource পুল/concurrency সীমা, যাতে একটা ধীর নির্ভরতা সব thread না খায়। (&lt;code&gt;ConcurrencyLimiter&lt;/code&gt;, আলাদা &lt;code&gt;HttpClient&lt;/code&gt;, আলাদা queue)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fallback:&lt;/strong&gt; ব্যর্থ হলে বিকল্প উত্তর।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graceful Degradation:&lt;/strong&gt; সব ফিচার না পারলেও মূল কাজ চালু রাখা।&lt;/p&gt;

&lt;p&gt;ShopEasy তে "Recommendation" সার্ভিস ডাউন হলে:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ পুরো পণ্য পেজ ৫০০ ফেরত দিল&lt;/li&gt;
&lt;li&gt;✅ "আপনার জন্য সুপারিশ" অংশ লুকিয়ে বা জনপ্রিয় পণ্যের cached তালিকা দেখিয়ে বাকি পেজ স্বাভাবিক&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;পেমেন্ট ডাউন থাকলে অর্ডার "Pending Payment" রেখে পরে সম্পন্ন করার সুযোগ দেওয়া যেতে পারে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.৬ Load Balancing ও Service Discovery
&lt;/h3&gt;

&lt;p&gt;মাইক্রোসার্ভিসে সার্ভিসের instance সংখ্যা ও IP প্রতিনিয়ত বদলায় (scale up/down, deploy)। কোডে hard-coded IP চলে না।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Discovery:&lt;/strong&gt; সার্ভিস নিজেকে নিবন্ধন করে; অন্যরা নাম দিয়ে খুঁজে নেয় (Consul, Kubernetes DNS, Eureka)। Kubernetes এ &lt;code&gt;http://payment-service&lt;/code&gt; লিখলেই DNS ঠিক instance এ পাঠায়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side বনাম Server-side load balancing:&lt;/strong&gt; ক্লায়েন্ট নিজেই তালিকা থেকে বেছে নেয়, বা মাঝে একটা load balancer থাকে।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Mesh (Istio, Linkerd):&lt;/strong&gt; retry, timeout, mTLS, tracing প্রতিটি সার্ভিসের পাশে &lt;strong&gt;sidecar proxy&lt;/strong&gt; দিয়ে কোডের বাইরে সামলায়। বড় সিস্টেমে কার্যকর, তবে জটিলতা বাড়ে।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YARP দিয়ে Gateway (.NET):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ReverseProxy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ClusterId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/orders/{**rest}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Clusters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"LoadBalancingPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RoundRobin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HealthCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/health"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Destinations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"d1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-1:8080"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"d2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://orders-2:8080"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৭.৭ Health Check
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisConn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"redis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Liveness: প্রক্রিয়াটা বেঁচে আছে? (ফেল করলে restart)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health/live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Readiness: ট্রাফিক নেওয়ার জন্য প্রস্তুত? (ফেল করলে load balancer ট্রাফিক বন্ধ রাখে)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health/ready"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Liveness আর Readiness আলাদা রাখুন। DB ডাউন হলে সব pod কে restart করা (liveness ফেল) সমাধান নয়; শুধু ট্রাফিক বন্ধ (readiness) যথেষ্ট।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.৮ Distributed System এর আরও মূল ধারণা
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Eventual Consistency এর সাথে বাঁচা
&lt;/h4&gt;

&lt;p&gt;মাইক্রোসার্ভিস ও replica তে ডেটা কিছুক্ষণ অসঙ্গত থাকতে পারে। UI/UX ও ব্যবসায়িক নিয়মে এটা মাথায় রাখুন:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;অর্ডার দেওয়ার পর পেজে "আপনার অর্ডার প্রক্রিয়াধীন" দেখান (লিস্টে আসতে সামান্য দেরি হতে পারে)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-your-writes&lt;/strong&gt; এর জন্য লেখার পর কিছুক্ষণ primary থেকে পড়ুন&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Leader Election ও Consensus (ধারণা)
&lt;/h4&gt;

&lt;p&gt;কয়েকটি সার্ভারের মধ্যে একজনকে "নেতা" বাছতে বা একটা মানে সবার একমত হতে &lt;strong&gt;Consensus অ্যালগরিদম&lt;/strong&gt; (Raft, Paxos) লাগে। আপনি সাধারণত নিজে লিখবেন না; Redis Sentinel, etcd, ZooKeeper, বা ক্লাউড-ম্যানেজড সার্ভিস ব্যবহার করবেন। শুধু জানুন: কেন একাধিক node এর অর্ধেকের বেশির সম্মতি (&lt;strong&gt;Quorum&lt;/strong&gt;) লাগে (যাতে network partition এ দুটো দল আলাদা সিদ্ধান্ত না নেয়, "split-brain")।&lt;/p&gt;

&lt;h4&gt;
  
  
  Distributed Lock
&lt;/h4&gt;

&lt;p&gt;একাধিক server এ একই কাজ একবারই চালাতে (যেমন রাতের রিপোর্ট)। Redis (&lt;code&gt;RedLock&lt;/code&gt;), DB row lock বা কোনো coordinator। সতর্কতা: লক মেয়াদ ও ক্র্যাশ সামলান (লকধারী মরে গেলে যেন লক চিরকাল না থাকে), আর সম্ভব হলে লকের বদলে idempotent ডিজাইনকে প্রাধান্য দিন।&lt;/p&gt;

&lt;h4&gt;
  
  
  ঘড়ির সমস্যা
&lt;/h4&gt;

&lt;p&gt;দুটো সার্ভারের ঘড়ি হুবহু এক নয়। "কে আগে লিখেছে" নির্ণয়ে সার্ভারের wall-clock এর ওপর ভরসা করবেন না। Logical clock, version number, বা DB এর ক্রম ব্যবহার করুন। সময় সংরক্ষণে সবসময় UTC।&lt;/p&gt;

&lt;h4&gt;
  
  
  Fan-out ও Tail Latency
&lt;/h4&gt;

&lt;p&gt;একটা request ১০টা সার্ভিস ডাকলে মোট ধীরতা নির্ভর করে &lt;strong&gt;সবচেয়ে ধীরটির&lt;/strong&gt; ওপর। প্রতিটির p99 ১% ধীর হলেও, ১০টা ডাকলে প্রায় ১০% request ধীর হবে। তাই: সমান্তরালে ডাকুন (&lt;code&gt;Task.WhenAll&lt;/code&gt;), ডাকার সংখ্যা কমান, hedged request (ধীর হলে দ্বিতীয় প্রতিলিপিতেও পাঠান), cache।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;productTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stockTask&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inventoryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reviewTask&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reviewClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stockTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reviewTask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// একটার পর একটা নয়, একসাথে&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৭.৯ Chaos Engineering ও পরীক্ষা
&lt;/h3&gt;

&lt;p&gt;ব্যর্থতা সামলানোর কোড যদি কখনো পরীক্ষা না হয়, প্রকৃত বিপদে কাজ করবে না। Netflix এর Chaos Monkey এর ধারণা: ইচ্ছাকৃতভাবে সার্ভার বন্ধ করে দেখুন সিস্টেম টেকে কি না। শুরুতে ছোট: staging এ একটা নির্ভরতা বন্ধ করে দেখুন কী ঘটে। .NET এ Polly এর Chaos strategies দিয়ে কৃত্রিম ব্যর্থতা/বিলম্ব ঢোকানো যায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৭.১০ Resilience চেকলিস্ট
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] প্রতিটি বাইরের call এ timeout আছে?&lt;/li&gt;
&lt;li&gt;[ ] Retry তে backoff ও jitter, এবং শুধু idempotent/retry-যোগ্য কাজে?&lt;/li&gt;
&lt;li&gt;[ ] Circuit breaker আছে?&lt;/li&gt;
&lt;li&gt;[ ] Bulkhead/concurrency সীমা দিয়ে একটা নির্ভরতাকে আলাদা রাখা হয়েছে?&lt;/li&gt;
&lt;li&gt;[ ] Fallback/graceful degradation ভাবা হয়েছে?&lt;/li&gt;
&lt;li&gt;[ ] Health check (liveness ও readiness আলাদা)?&lt;/li&gt;
&lt;li&gt;[ ] সব queue consumer idempotent?&lt;/li&gt;
&lt;li&gt;[ ] ব্যর্থতার পরিস্থিতি পরীক্ষা করা হয়েছে?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 7 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cascading failure ঠেকাতে Timeout, Retry (backoff+jitter), Circuit Breaker, Bulkhead, Fallback একসাথে ব্যবহার করুন।&lt;/li&gt;
&lt;li&gt;.NET এ &lt;code&gt;AddStandardResilienceHandler&lt;/code&gt; দিয়ে দ্রুত শুরু।&lt;/li&gt;
&lt;li&gt;Service Discovery ও Health Check মাইক্রোসার্ভিসের ভিত্তি।&lt;/li&gt;
&lt;li&gt;ঘড়ি, নেটওয়ার্ক, ক্রম কিছুই নিখুঁত নয়; সেই অনুযায়ী ডিজাইন করুন।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 8: Security: নিরাপত্তা শুরু থেকেই
&lt;/h2&gt;

&lt;p&gt;শেষে যোগ করা নিরাপত্তা সবসময় ফাঁকা থাকে। ShopEasy তে একদিন দেখা গেল, ইউজার URL এ &lt;code&gt;/orders/5&lt;/code&gt; কে &lt;code&gt;/orders/6&lt;/code&gt; করে অন্যের অর্ডার দেখতে পারছে। ভয়াবহ। এই Part এ নিরাপত্তার মৌলিক ও বাস্তব দিক।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.১ মৌলিক ধারণা
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication (AuthN):&lt;/strong&gt; আপনি কে? (পরিচয় যাচাই: login)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization (AuthZ):&lt;/strong&gt; আপনি কী করতে পারবেন? (অনুমতি)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;উপমা: অফিসে ঢুকতে আইডি কার্ড দেখানো (AuthN), আর কোন ফ্লোরে যেতে পারবেন তা কার্ডের অনুমতি (AuthZ)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CIA Triad:&lt;/strong&gt; Confidentiality (গোপনীয়তা), Integrity (অখণ্ডতা), Availability (প্রাপ্যতা)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;মূলনীতি:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Least Privilege:&lt;/strong&gt; শুধু যতটুকু দরকার ততটুকু অনুমতি&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defense in Depth:&lt;/strong&gt; একাধিক স্তরে নিরাপত্তা, একটা ভাঙলে অন্যটা বাঁচায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Trust:&lt;/strong&gt; ভেতরের নেটওয়ার্ক বলেও কাউকে অন্ধ বিশ্বাস নয়; প্রতিটি request যাচাই&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure by Default&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৮.২ Password ও Credential
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;কখনো password সরল টেক্সটে রাখবেন না; এমনকি সাধারণ hash (MD5/SHA-256) ও যথেষ্ট নয়।&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ব্যবহার করুন ধীর, salt-যুক্ত অ্যালগরিদম: &lt;strong&gt;bcrypt, scrypt, Argon2, PBKDF2&lt;/strong&gt;। ASP.NET Core Identity এর &lt;code&gt;PasswordHasher&lt;/code&gt; এটা সামলায়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salt&lt;/strong&gt; হলো প্রতিটি password এর সাথে র‍্যান্ডম মান, যাতে একই password এর hash আলাদা হয় ও rainbow table অকার্যকর।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;আরও:&lt;/strong&gt; Multi-Factor Authentication (MFA), পাসওয়ার্ড লিক-তালিকার সাথে মিলিয়ে দেখা, ব্যর্থ লগইনে lockout/rate limit।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.৩ Session, Cookie ও Token
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cookie-ভিত্তিক Session:&lt;/strong&gt; সার্ভার session মনে রাখে, ব্রাউজারে ID পাঠায়। ব্রাউজার-অ্যাপে সহজ ও নিরাপদ (সঠিক ফ্ল্যাগে)। কিন্তু একাধিক সার্ভারে session শেয়ার করতে লাগে (Redis)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token-ভিত্তিক (JWT):&lt;/strong&gt; সার্ভার কিছু মনে রাখে না (stateless); token নিজেই তথ্য বহন করে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT (JSON Web Token) এর গঠন:&lt;/strong&gt; &lt;code&gt;header.payload.signature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;header   = { "alg": "RS256", "typ": "JWT" }
payload  = { "sub": "42", "name": "Rafi", "role": "admin", "exp": 1790000000 }
signature= সাইন করা (গোপন কী দিয়ে), যাতে কেউ বদলালে ধরা পড়ে
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;গুরুত্বপূর্ণ:&lt;/strong&gt; JWT এর payload &lt;strong&gt;এনক্রিপ্ট করা নয়&lt;/strong&gt;, শুধু সাইন করা; যে কেউ পড়তে পারে। তাই গোপন কিছু রাখবেন না।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT এর ঝুঁকি ও সর্বোত্তম অভ্যাস:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access token ছোট মেয়াদী&lt;/strong&gt; (৫-১৫ মিনিট); দীর্ঘ মেয়াদে &lt;strong&gt;Refresh token&lt;/strong&gt; (নিরাপদ জায়গায়, rotation সহ)&lt;/li&gt;
&lt;li&gt;একবার ইস্যু করা token সহজে বাতিল করা যায় না; তাই ছোট মেয়াদ ও দরকারে denylist/revocation&lt;/li&gt;
&lt;li&gt;ব্রাউজারে &lt;code&gt;localStorage&lt;/code&gt; এ token রাখলে XSS এ চুরির ঝুঁকি; &lt;code&gt;HttpOnly, Secure, SameSite&lt;/code&gt; cookie নিরাপদ বিকল্প&lt;/li&gt;
&lt;li&gt;অ্যালগরিদম ও &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt;, &lt;code&gt;exp&lt;/code&gt; সবসময় যাচাই করুন
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://auth.shopeasy.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Audience&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"shopeasy-api"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ValidateIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ClockSkew&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// এই ক্রম গুরুত্বপূর্ণ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৮.৪ OAuth 2.0 ও OpenID Connect (OIDC)
&lt;/h3&gt;

&lt;p&gt;আপনি "Google দিয়ে Login" ক্লিক করেন। ShopEasy আপনার Google password কখনো দেখে না। এটাই OAuth/OIDC।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OAuth 2.0:&lt;/strong&gt; &lt;strong&gt;Authorization&lt;/strong&gt; এর ফ্রেমওয়ার্ক। "এই অ্যাপকে আমার ডেটায় সীমিত অ্যাক্সেস দিন।"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenID Connect:&lt;/strong&gt; OAuth এর ওপর &lt;strong&gt;Authentication&lt;/strong&gt; স্তর (ইউজার কে, তা জানায়)।&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authorization Code Flow with PKCE&lt;/strong&gt; (ব্রাউজার/মোবাইল ও আজকের সেরা অনুশীলন):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ব্যবহারকারীকে Identity Provider (IdP) এ পাঠানো হয়, সেখানে login&lt;/li&gt;
&lt;li&gt;IdP একটা &lt;strong&gt;code&lt;/strong&gt; ফেরত পাঠায়&lt;/li&gt;
&lt;li&gt;অ্যাপ সেই code বিনিময়ে &lt;strong&gt;access token (+ ID token, refresh token)&lt;/strong&gt; নেয় (PKCE এই বিনিময় চুরি থেকে রক্ষা করে)&lt;/li&gt;
&lt;li&gt;অ্যাপ access token দিয়ে API ডাকে&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Client Credentials Flow:&lt;/strong&gt; সার্ভার-থেকে-সার্ভার (কোনো ইউজার নেই)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;নিজের Identity Server না বানানো ভালো।&lt;/strong&gt; ম্যানেজড সার্ভিস (Microsoft Entra ID, Auth0, Keycloak, AWS Cognito) বা মানসম্মত লাইব্রেরি ব্যবহার করুন। ASP.NET Core Identity নিজস্ব ইউজার ডেটাবেসের জন্য, .NET 8+ এ Identity API endpoints আছে। (Duende IdentityServer শক্তিশালী কিন্তু বাণিজ্যিক লাইসেন্স লাগতে পারে।)&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.৫ Authorization: Role, Claim, Policy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Role-based&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Admin"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="c1"&gt;// Policy-based (নমনীয়, প্রস্তাবিত)&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorizationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CanRefund"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"permission"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orders.refund"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adult"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireAssertion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* বয়স ≥ 18 */&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id}/refund"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Refund&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CanRefund"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সবচেয়ে সাধারণ ও বিপজ্জনক ভুল: BOLA/IDOR (Broken Object Level Authorization)।&lt;/strong&gt; মানে "এই ইউজার এই নির্দিষ্ট রেকর্ডের মালিক কি না" যাচাই না করা। ShopEasy তে &lt;code&gt;/orders/5&lt;/code&gt; সমস্যা ছিল এটাই।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ শুধু login যথেষ্ট ধরা হয়েছে&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ মালিকানা যাচাই&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClaimsPrincipal&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindFirstValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameIdentifier&lt;/span&gt;&lt;span class="p"&gt;)!);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// অন্যের হলে 404, অস্তিত্বও ফাঁস নয়&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;সাধারণত Controller এ নয়, query তেই মালিকানা ফিল্টার রাখুন। বড় সিস্টেমে resource-based authorization (&lt;code&gt;IAuthorizationService&lt;/code&gt;) ব্যবহার।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.৬ OWASP Top 10: বাস্তবে যা যা ঘটে
&lt;/h3&gt;

&lt;p&gt;OWASP হলো ওয়েব নিরাপত্তার সবচেয়ে সুপরিচিত তালিকা (সংস্করণ ও ক্রম হালনাগাদ হয়; বর্তমান তালিকা OWASP এর সাইটে দেখুন)। মূল ঝুঁকিগুলো:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ঝুঁকি&lt;/th&gt;
&lt;th&gt;কী&lt;/th&gt;
&lt;th&gt;প্রতিরোধ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Broken Access Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;অনুমতি ছাড়া অন্যের ডেটা/কাজ&lt;/td&gt;
&lt;td&gt;প্রতিটি request এ সার্ভারে মালিকানা/role যাচাই; default deny&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Injection (SQL)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ইনপুটে ঢোকানো SQL কোড&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Parameterized query&lt;/strong&gt;; কখনো string জুড়ে SQL নয়&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cryptographic Failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;দুর্বল/অনুপস্থিত এনক্রিপশন&lt;/td&gt;
&lt;td&gt;HTTPS সর্বত্র; আধুনিক অ্যালগরিদম; সংবেদনশীল ডেটা এনক্রিপ্ট&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Insecure Design&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;নকশাতেই ত্রুটি&lt;/td&gt;
&lt;td&gt;Threat modeling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security Misconfiguration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ডিফল্ট পাসওয়ার্ড, খোলা debug&lt;/td&gt;
&lt;td&gt;হার্ডেনিং, ন্যূনতম এক্সপোজার&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vulnerable Components&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;পুরনো/ঝুঁকিপূর্ণ NuGet প্যাকেজ&lt;/td&gt;
&lt;td&gt;নিয়মিত আপডেট, &lt;code&gt;dotnet list package --vulnerable&lt;/code&gt;, Dependabot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication Failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;দুর্বল login&lt;/td&gt;
&lt;td&gt;MFA, rate limit, lockout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integrity Failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;সাইন-না-করা আপডেট/ডেটা&lt;/td&gt;
&lt;td&gt;সাইন যাচাই, CI/CD সুরক্ষা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logging Failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;আক্রমণ ধরা পড়ে না&lt;/td&gt;
&lt;td&gt;নিরাপত্তা ঘটনা লগ ও alert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSRF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;সার্ভারকে দিয়ে ভেতরের URL ডাকানো&lt;/td&gt;
&lt;td&gt;বাইরে থেকে পাওয়া URL এর allow-list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SQL Injection উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ ভয়ংকর: ইনপুট "x'; DROP TABLE Users;--"&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"SELECT * FROM Users WHERE Name = '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ EF Core LINQ (স্বয়ংক্রিয় parameter)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Raw SQL হলেও parameterized&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSqlInterpolated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"SELECT * FROM Users WHERE Name = &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// (FromSqlRaw এ string interpolation কখনো নয়)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;XSS (Cross-Site Scripting):&lt;/strong&gt; আক্রমণকারী ওয়েবপেজে স্ক্রিপ্ট ঢোকায়। প্রতিরোধ: আউটপুট এনকোড (Razor/Blazor স্বয়ংক্রিয় করে; &lt;code&gt;Html.Raw&lt;/code&gt; এ সতর্ক), Content-Security-Policy header, ইনপুট যাচাই।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSRF (Cross-Site Request Forgery):&lt;/strong&gt; ভুক্তভোগীর login অবস্থা ব্যবহার করে অন্য সাইট থেকে জাল request। প্রতিরোধ: SameSite cookie, Anti-forgery token (&lt;code&gt;[ValidateAntiForgeryToken]&lt;/code&gt;; cookie-ভিত্তিক সাইটে জরুরি)। Bearer-token API তে সাধারণত CSRF সমস্যা কম।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mass Assignment:&lt;/strong&gt; DTO ব্যবহার করুন (Part 5)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.৭ Secret ও Configuration ব্যবস্থাপনা
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Connection string, API key, password কখনো Git এ কমিট করবেন না।&lt;/strong&gt; (একবার কমিট হলে ইতিহাসে থেকে যায়; ধরে নিন ফাঁস হয়েছে, সাথে সাথে বদলান।)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ডেভেলপমেন্ট:&lt;/strong&gt; &lt;code&gt;dotnet user-secrets&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;প্রোডাকশন:&lt;/strong&gt; Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets (এনক্রিপশন সক্রিয় করে)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Identity:&lt;/strong&gt; অ্যাপ নিজেই ক্লাউডের পরিচয়ে Key Vault/DB তে ঢোকে, কোনো password কোডে বা কনফিগে নেই। সবচেয়ে নিরাপদ।&lt;/li&gt;
&lt;li&gt;Secret নিয়মিত rotate করুন; লগে কখনো secret প্রিন্ট নয়।
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAzureKeyVault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://shopeasy-kv.vault.azure.net/"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;   &lt;span class="c1"&gt;// Managed Identity/dev credential&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ৮.৮ ডেটা সুরক্ষা ও গোপনীয়তা
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption in Transit:&lt;/strong&gt; সর্বত্র HTTPS/TLS (&lt;code&gt;app.UseHttpsRedirection()&lt;/code&gt;, HSTS)। সার্ভিস-থেকে-সার্ভিস mTLS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption at Rest:&lt;/strong&gt; DB/ডিস্ক এনক্রিপশন; সংবেদনশীল কলাম (NID, কার্ড) আলাদা এনক্রিপ্ট&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;কার্ডের তথ্য নিজে রাখবেন না;&lt;/strong&gt; PCI-DSS জটিল। Payment gateway এর tokenization ব্যবহার করুন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII (ব্যক্তিগত তথ্য):&lt;/strong&gt; শুধু প্রয়োজনীয়টুকু সংগ্রহ (Data Minimization), লগে মাস্ক, আইন (যেমন GDPR বা স্থানীয় ডেটা সুরক্ষা আইন) অনুযায়ী মুছে ফেলা/রপ্তানির সুযোগ রাখুন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASP.NET Core Data Protection API:&lt;/strong&gt; cookie/token এনক্রিপশনের কী একাধিক সার্ভারে শেয়ার করুন (নইলে এক সার্ভারের cookie অন্যটি পড়তে পারে না)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৮.৯ API ও অবকাঠামো সুরক্ষা
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CORS:&lt;/strong&gt; শুধু বিশ্বস্ত origin অনুমোদন (&lt;code&gt;AllowAnyOrigin&lt;/code&gt; + credentials এক সাথে নয়)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Headers:&lt;/strong&gt; &lt;code&gt;Strict-Transport-Security&lt;/code&gt;, &lt;code&gt;Content-Security-Policy&lt;/code&gt;, &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;, &lt;code&gt;X-Frame-Options&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting ও WAF&lt;/strong&gt; (Web Application Firewall: Cloudflare, Azure Front Door) বট ও সাধারণ আক্রমণ ঠেকায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ইনপুট সীমা:&lt;/strong&gt; request body এর আকার সীমা, ফাইল আপলোডে type/আকার/ভাইরাস স্ক্যান, সংরক্ষণ ওয়েব-রুটের বাইরে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container নিরাপত্তা:&lt;/strong&gt; non-root ইউজার, ছোট base image, image স্ক্যান&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;নেটওয়ার্ক:&lt;/strong&gt; DB ইন্টারনেটে খোলা নয়; private network/VNet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency নিরাপত্তা:&lt;/strong&gt; &lt;code&gt;dotnet list package --vulnerable&lt;/code&gt;, Dependabot/Renovate, SBOM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Log:&lt;/strong&gt; কে কখন কী করল (বিশেষত অ্যাডমিন কাজ)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৮.১০ Threat Modeling: STRIDE
&lt;/h3&gt;

&lt;p&gt;ডিজাইনের সময়ই জিজ্ঞেস করুন "কী ভুল হতে পারে?" STRIDE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt;poofing (ভুয়া পরিচয়)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;T&lt;/strong&gt;ampering (ডেটা বদল)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R&lt;/strong&gt;epudiation ("আমি করিনি" অস্বীকার; audit log সমাধান)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;nformation Disclosure (তথ্য ফাঁস)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;enial of Service (সেবা বন্ধ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E&lt;/strong&gt;levation of Privilege (অনুমতি বাড়ানো)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;প্রতিটি data flow ও trust boundary তে এই ছয়টি প্রশ্ন করুন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৮.১১ নিরাপত্তা চেকলিস্ট
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] সর্বত্র HTTPS, HSTS&lt;/li&gt;
&lt;li&gt;[ ] Password Argon2/bcrypt/PBKDF2 দিয়ে হ্যাশ; MFA সমর্থন&lt;/li&gt;
&lt;li&gt;[ ] প্রতিটি endpoint এ authentication ও resource-level authorization (BOLA)&lt;/li&gt;
&lt;li&gt;[ ] সব ইনপুট যাচাই; DTO ব্যবহার; parameterized query&lt;/li&gt;
&lt;li&gt;[ ] Secret কোডে নেই; Key Vault/Managed Identity&lt;/li&gt;
&lt;li&gt;[ ] CORS, security header, rate limiting&lt;/li&gt;
&lt;li&gt;[ ] Dependency নিয়মিত আপডেট ও স্ক্যান&lt;/li&gt;
&lt;li&gt;[ ] সংবেদনশীল ডেটা এনক্রিপ্ট; লগে PII/secret নেই&lt;/li&gt;
&lt;li&gt;[ ] Audit log ও নিরাপত্তা alert&lt;/li&gt;
&lt;li&gt;[ ] Error বার্তায় ভেতরের তথ্য (stack trace) ফাঁস হয় না&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 8 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AuthN ≠ AuthZ; সবচেয়ে বড় ঝুঁকি অনুমতি যাচাই ভুলে যাওয়া (BOLA)।&lt;/li&gt;
&lt;li&gt;নিজে crypto বা identity server বানাবেন না; প্রমাণিত সমাধান ব্যবহার করুন।&lt;/li&gt;
&lt;li&gt;Secret কোডের বাইরে, Managed Identity সবচেয়ে ভালো।&lt;/li&gt;
&lt;li&gt;Defense in Depth ও Least Privilege; ডিজাইনের সময় threat model।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 9: Observability, DevOps ও Cloud
&lt;/h2&gt;

&lt;p&gt;রাত ২টায় ফোন: "সাইট ধীর!" আপনি জানেন না কোথায় সমস্যা: কোন সার্ভিস, কোন query, কোন সার্ভার। &lt;strong&gt;যা মাপা যায় না, তা ঠিকও করা যায় না।&lt;/strong&gt; এই Part এ শিখব সিস্টেমকে "দেখার" উপায় ও নিরাপদে deploy করার পদ্ধতি।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.১ Observability এর তিন স্তম্ভ
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;স্তম্ভ&lt;/th&gt;
&lt;th&gt;কী বলে&lt;/th&gt;
&lt;th&gt;উদাহরণ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;কী ঘটেছে (ঘটনার বিবরণ)&lt;/td&gt;
&lt;td&gt;"Order 5 payment failed: timeout"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;কতটা/কত ঘন (সংখ্যা, সময়ের সাথে)&lt;/td&gt;
&lt;td&gt;request/sec, error rate, p99 latency, CPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;একটা request পুরো সিস্টেমে কোথায় কত সময় নিল&lt;/td&gt;
&lt;td&gt;Gateway 5ms → Order 20ms → Payment 3000ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt; জানে "কী ভাঙছে" (পূর্বনির্ধারিত প্রশ্ন); &lt;strong&gt;Observability&lt;/strong&gt; আপনাকে নতুন, অপ্রত্যাশিত প্রশ্ন করতে দেয় ("কেন শুধু বুধবারে মোবাইল ইউজাররা ধীর?")।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.২ Logging: Serilog ও Structured Logging
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unstructured log (খারাপ):&lt;/strong&gt; &lt;code&gt;"User 42 placed order 5 for 1200"&lt;/code&gt;: খুঁজতে regex লাগে।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured log (ভালো):&lt;/strong&gt; ফিল্ডসহ, query করা যায়।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ string interpolation: structured হয় না&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Order &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; placed by &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ message template: id ও userId আলাদা searchable ফিল্ড&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order {OrderId} placed by {UserId} total {Total}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Serilog সেটআপ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSerilog&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFrom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromLogContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ordering"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CompactJsonFormatter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://seq:5341"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;        &lt;span class="c1"&gt;// বা Elasticsearch, Loki, Application Insights&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSerilogRequestLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Log Level:&lt;/strong&gt; &lt;code&gt;Trace &amp;lt; Debug &amp;lt; Information &amp;lt; Warning &amp;lt; Error &amp;lt; Critical&lt;/code&gt;। Production এ সাধারণত Information+।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;লগের নিয়ম:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Correlation ID/Trace ID&lt;/strong&gt; প্রতিটি লগে, যাতে এক request এর সব লগ জোড়া যায়&lt;/li&gt;
&lt;li&gt;Password, token, কার্ড, PII লগ নয়&lt;/li&gt;
&lt;li&gt;Exception এ stack trace সহ &lt;code&gt;log.LogError(ex, "...")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;অতিরিক্ত লগ খরচ ও গোলমাল বাড়ায়; অর্থবহ ঘটনা লগ করুন&lt;/li&gt;
&lt;li&gt;কেন্দ্রীয় লগ সংগ্রহ (Seq, ELK/Elastic, Grafana Loki, Azure Monitor); প্রতিটি সার্ভারে ঢুকে ফাইল পড়া নয়&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৯.৩ Metrics ও চার সোনালি সংকেত
&lt;/h3&gt;

&lt;p&gt;Google SRE এর "Four Golden Signals":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; কত সময় লাগছে (p50, p95, p99)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic:&lt;/strong&gt; কত request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errors:&lt;/strong&gt; কত শতাংশ ব্যর্থ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saturation:&lt;/strong&gt; সম্পদ কতটা ভরা (CPU, মেমরি, DB connection pool, queue গভীরতা)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;আরও পরিচিত কাঠামো: &lt;strong&gt;RED&lt;/strong&gt; (Rate, Errors, Duration) সার্ভিসের জন্য; &lt;strong&gt;USE&lt;/strong&gt; (Utilization, Saturation, Errors) সম্পদের জন্য।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// কাস্টম business metric (System.Diagnostics.Metrics)&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderMetrics&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_placed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMeterFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;meter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ShopEasy.Orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_placed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateCounter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders.placed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OrderPlaced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_placed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"payment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Business metric ও গুরুত্বপূর্ণ:&lt;/strong&gt; শুধু CPU নয়, "প্রতি মিনিটে কত অর্ডার" দেখুন। অর্ডার হঠাৎ শূন্যে নামলে সার্ভার সুস্থ থাকলেও সমস্যা আছে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.৪ Distributed Tracing ও OpenTelemetry
&lt;/h3&gt;

&lt;p&gt;মাইক্রোসার্ভিসে একটা অর্ডার ৫টা সার্ভিস ঘোরে। ধীর কোথায়? &lt;strong&gt;Trace&lt;/strong&gt; এক request এর পুরো যাত্রার ম্যাপ। প্রতিটি ধাপ একটা &lt;strong&gt;span&lt;/strong&gt;, সবাই একই &lt;strong&gt;Trace ID&lt;/strong&gt; শেয়ার করে (HTTP header &lt;code&gt;traceparent&lt;/code&gt; দিয়ে এক সার্ভিস থেকে পরেরটায় পৌঁছায়, W3C Trace Context)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenTelemetry (OTel)&lt;/strong&gt; হলো এই বিষয়ে মান ও ভেন্ডর-নিরপেক্ষ সমাধান; .NET এ এটাই প্রচলিত পথ।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenTelemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ordering"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEntityFrameworkCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ShopEasy.Orders"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;                 &lt;span class="c1"&gt;// Jaeger, Tempo, Azure Monitor, Datadog ইত্যাদিতে&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRuntimeInstrumentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ShopEasy.Orders"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOtlpExporter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;নিজের span:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ActivitySource&lt;/span&gt; &lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ShopEasy.Orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CalculateDiscount"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SetTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order.id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;জনপ্রিয় টুল: &lt;strong&gt;Prometheus + Grafana&lt;/strong&gt; (metrics), &lt;strong&gt;Jaeger/Tempo&lt;/strong&gt; (trace), &lt;strong&gt;Loki/Seq/ELK&lt;/strong&gt; (log), Azure Application Insights, Datadog, New Relic। .NET Aspire dashboard স্থানীয় ডেভেলপমেন্টে এগুলো একসাথে দেখায়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.৫ Alerting ও Incident Response
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alert হবে লক্ষণে (symptom), কারণে নয়:&lt;/strong&gt; "error rate &amp;gt; ৫%" বা "p99 &amp;gt; ২ সেকেন্ড" (ব্যবহারকারী ক্ষতিগ্রস্ত)। "CPU ৮০%" একা কোনো সমস্যা নয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alert fatigue এড়ান:&lt;/strong&gt; বেশি alert মানে কেউ আর পাত্তা দেয় না। শুধু যেগুলোতে &lt;strong&gt;তৎক্ষণাৎ মানুষের পদক্ষেপ&lt;/strong&gt; লাগে, সেগুলো pager এ।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLO ও Error Budget:&lt;/strong&gt; SLO ৯৯.৯% মানে মাসে ~৪৩ মিনিট ব্যর্থতা "বাজেট"। বাজেট ফুরালে নতুন ফিচার থামিয়ে স্থিতিশীলতায় মন দিন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runbook:&lt;/strong&gt; প্রতিটি alert এর সাথে "কী করবেন" লিখিত।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blameless Postmortem:&lt;/strong&gt; ঘটনার পর দোষারোপ নয়, "কী শিখলাম ও কী বদলাব"।&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৯.৬ Docker: যেখানে চলবে সেই পরিবেশসহ প্যাকেজ
&lt;/h3&gt;

&lt;p&gt;"আমার মেশিনে চলে!" সমস্যার সমাধান। &lt;strong&gt;Container&lt;/strong&gt; অ্যাপ, তার runtime ও dependency একসাথে প্যাকেজ করে; যেকোনো জায়গায় একইভাবে চলে। ভার্চুয়াল মেশিনের চেয়ে হালকা (OS kernel শেয়ার করে)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-stage Dockerfile (.NET):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ধাপ ১: build (SDK দিয়ে, বড় image)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:9.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; *.csproj .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore                      &lt;span class="c"&gt;# আলাদা ধাপে, যাতে layer cache কাজে লাগে&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app &lt;span class="nt"&gt;--no-restore&lt;/span&gt;

&lt;span class="c"&gt;# ধাপ ২: final (শুধু runtime, ছোট ও নিরাপদ)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:9.0&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app .&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; $APP_UID                           # non-root&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "ShopEasy.Api.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Docker Compose&lt;/strong&gt; দিয়ে স্থানীয়ভাবে পুরো সিস্টেম (API + SQL + Redis + RabbitMQ) একসাথে:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ConnectionStrings__Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server=db;Database=shop;User=sa;Password=${DB_PASSWORD};TrustServerCertificate=true"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcr.microsoft.com/mssql/server:2022-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y"&lt;/span&gt;
      &lt;span class="na"&gt;MSSQL_SA_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt; Container stateless ও disposable (যেকোনো মুহূর্তে মরতে/নতুন হতে পারে); কনফিগ environment variable এ; লগ stdout এ; secret image এর ভেতরে নয়।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.৭ Kubernetes (K8s): Container এর ম্যানেজার
&lt;/h3&gt;

&lt;p&gt;শত শত container কে কে চালাবে, ফেল করলে কে আবার তুলবে, লোড বাড়লে কে বাড়াবে? Kubernetes।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;মূল ধারণা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pod:&lt;/strong&gt; একটা বা একাধিক container এর ক্ষুদ্রতম একক&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; কয়টা pod চলবে ও কীভাবে আপডেট হবে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service:&lt;/strong&gt; pod গুলোর সামনে স্থির নাম/IP ও load balancing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress:&lt;/strong&gt; বাইরের ট্রাফিকের প্রবেশপথ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ConfigMap/Secret:&lt;/strong&gt; কনফিগ ও গোপন তথ্য&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HPA (Horizontal Pod Autoscaler):&lt;/strong&gt; CPU/মেট্রিক দেখে pod বাড়ায়-কমায়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Namespace:&lt;/strong&gt; পরিবেশ/টিম আলাদা করা
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ordering&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;           &lt;span class="c1"&gt;# একটা একটা করে pod বদলায়, downtime নেই&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ordering&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ordering&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shopeasy/ordering:1.4.2&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[{&lt;/span&gt; &lt;span class="nv"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;8080&lt;/span&gt; &lt;span class="pi"&gt;}]&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500m"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;/health/live&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;  &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;8080&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;/health/ready&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;8080&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;সতর্কতা:&lt;/strong&gt; Kubernetes শক্তিশালী কিন্তু জটিল, শেখা ও পরিচালনার খরচ বেশি। ছোট টিমের জন্য ম্যানেজড বিকল্প (Azure App Service, Azure Container Apps, AWS ECS/Fargate, Google Cloud Run) প্রায়ই যথেষ্ট ও সহজ। প্রয়োজন প্রমাণিত না হলে K8s এড়ান।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.৮ CI/CD: স্বয়ংক্রিয় build, test ও deploy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI (Continuous Integration):&lt;/strong&gt; প্রতিটি কোড পরিবর্তনে স্বয়ংক্রিয়ভাবে build ও test; সমস্যা তাড়াতাড়ি ধরা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CD (Continuous Delivery/Deployment):&lt;/strong&gt; পাস করা কোড স্বয়ংক্রিয়ভাবে (বা এক ক্লিকে) প্রোডাকশনে&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;একটা আদর্শ pipeline:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code commit/PR&lt;/li&gt;
&lt;li&gt;Build + restore&lt;/li&gt;
&lt;li&gt;Unit test + coverage&lt;/li&gt;
&lt;li&gt;স্ট্যাটিক বিশ্লেষণ ও dependency/নিরাপত্তা স্ক্যান&lt;/li&gt;
&lt;li&gt;Docker image build ও registry তে push&lt;/li&gt;
&lt;li&gt;Staging এ deploy + integration/smoke test&lt;/li&gt;
&lt;li&gt;অনুমোদন (দরকারে) → Production deploy&lt;/li&gt;
&lt;li&gt;Health check যাচাই; ব্যর্থ হলে স্বয়ংক্রিয় rollback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions উদাহরণ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ci&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9.0.x"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet restore&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build --no-restore -c Release&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet test --no-build -c Release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;অন্যান্য: Azure DevOps Pipelines, GitLab CI, Jenkins। &lt;strong&gt;Infrastructure as Code (IaC):&lt;/strong&gt; অবকাঠামো কোডে (Terraform, Bicep, Pulumi), ক্লিক করে নয়; ভার্সন নিয়ন্ত্রিত ও পুনরুৎপাদনযোগ্য।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.৯ নিরাপদ Deployment কৌশল
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;কৌশল&lt;/th&gt;
&lt;th&gt;কীভাবে&lt;/th&gt;
&lt;th&gt;সুবিধা/অসুবিধা&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rolling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;একটা একটা instance বদলানো&lt;/td&gt;
&lt;td&gt;সহজ; পুরনো ও নতুন ভার্সন কিছুক্ষণ একসাথে (সামঞ্জস্য লাগে)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blue-Green&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;দুটো পূর্ণ পরিবেশ; ট্রাফিক একবারে সুইচ&lt;/td&gt;
&lt;td&gt;তাৎক্ষণিক rollback; খরচ দ্বিগুণ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;প্রথমে ৫% ট্রাফিক নতুন ভার্সনে, ঠিক থাকলে বাড়ানো&lt;/td&gt;
&lt;td&gt;ঝুঁকি কম; জটিল&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature Flag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;কোড deploy করে ফিচার বন্ধ রাখা, পরে চালু&lt;/td&gt;
&lt;td&gt;deploy ও release আলাদা; ঝামেলা হলে সুইচ অফ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Database পরিবর্তন সবসময় backward-compatible রাখুন (Part 4 এর Expand-Contract), নইলে rolling deploy এ ভাঙবে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.১০ Cloud ধারণা
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;সেবার স্তর:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IaaS:&lt;/strong&gt; ভার্চুয়াল মেশিন (আপনি OS থেকে সামলান)। যেমন Azure VM, EC2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PaaS:&lt;/strong&gt; প্ল্যাটফর্ম (আপনি শুধু কোড দেন)। যেমন Azure App Service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FaaS/Serverless:&lt;/strong&gt; ফাংশন। যেমন Azure Functions, Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS:&lt;/strong&gt; তৈরি সফটওয়্যার&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Azure বনাম AWS (মূল সেবার সমতুল্যতা, .NET প্রসঙ্গে):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;কাজ&lt;/th&gt;
&lt;th&gt;Azure&lt;/th&gt;
&lt;th&gt;AWS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ওয়েব অ্যাপ হোস্ট&lt;/td&gt;
&lt;td&gt;App Service&lt;/td&gt;
&lt;td&gt;Elastic Beanstalk / App Runner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container&lt;/td&gt;
&lt;td&gt;Container Apps / AKS&lt;/td&gt;
&lt;td&gt;ECS-Fargate / EKS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless&lt;/td&gt;
&lt;td&gt;Functions&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL DB&lt;/td&gt;
&lt;td&gt;Azure SQL&lt;/td&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NoSQL&lt;/td&gt;
&lt;td&gt;Cosmos DB&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;Azure Cache for Redis&lt;/td&gt;
&lt;td&gt;ElastiCache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queue/Bus&lt;/td&gt;
&lt;td&gt;Service Bus&lt;/td&gt;
&lt;td&gt;SQS / SNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ফাইল&lt;/td&gt;
&lt;td&gt;Blob Storage&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret&lt;/td&gt;
&lt;td&gt;Key Vault&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;Application Insights&lt;/td&gt;
&lt;td&gt;CloudWatch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(সেবার নাম ও ফিচার বদলায়; সিদ্ধান্তের আগে সংশ্লিষ্ট ক্লাউডের হালনাগাদ ডকুমেন্ট দেখুন।)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud এর গুরুত্বপূর্ণ ধারণা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Region ও Availability Zone (AZ):&lt;/strong&gt; Region ভৌগোলিক এলাকা, AZ সেই এলাকার ভেতরের আলাদা ডেটাসেন্টার। একাধিক AZ তে চালালে একটা ডেটাসেন্টার গেলেও টিকে থাকে। একাধিক Region তে Disaster Recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scaling:&lt;/strong&gt; লোড অনুযায়ী স্বয়ংক্রিয় বাড়া-কমা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Management:&lt;/strong&gt; ক্লাউড খরচ নীরবে বাড়ে। বাজেট alert, অপ্রয়োজনীয় রিসোর্স বন্ধ, Reserved/Spot instance বিবেচনা, ডেটা ট্রান্সফার ফি লক্ষ্য রাখা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-Architected Framework:&lt;/strong&gt; Azure/AWS দুটোরই ডিজাইন নির্দেশিকা (নির্ভরযোগ্যতা, নিরাপত্তা, খরচ, পারফরম্যান্স, অপারেশনাল শ্রেষ্ঠত্ব)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy:&lt;/strong&gt; একই অ্যাপে একাধিক গ্রাহককে (SaaS) সেবা; ডেটা বিচ্ছিন্নতার মডেল (shared DB with TenantId, schema per tenant, DB per tenant)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ৯.১১ .NET Aspire
&lt;/h3&gt;

&lt;p&gt;.NET Aspire হলো ক্লাউড-নেটিভ, বিতরিত অ্যাপ বানানোর একটা অ-মতবাদী (opinionated) stack। স্থানীয়ভাবে একাধিক সার্ভিস, DB, Redis, RabbitMQ একসাথে চালানো, service discovery, কনফিগ, OpenTelemetry ও ড্যাশবোর্ড প্রায় স্বয়ংক্রিয়ভাবে মেলে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AppHost প্রজেক্ট: সিস্টেমের গঠন কোডে&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributedApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cache"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sql"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"shopdb"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordering_Api&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"ordering"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Web&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// service discovery স্বয়ংক্রিয়&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;মাইক্রোসার্ভিস বা একাধিক নির্ভরতার প্রজেক্টে ডেভেলপার অভিজ্ঞতা চমৎকার করে। (Aspire দ্রুত বিবর্তিত হচ্ছে; ব্যবহারের আগে সর্বশেষ ডকুমেন্ট দেখুন।)&lt;/p&gt;

&lt;h3&gt;
  
  
  ৯.১২ Configuration ও 12-Factor App
&lt;/h3&gt;

&lt;p&gt;আধুনিক ক্লাউড অ্যাপের নীতিমালা (সবগুলো না মানলেও মূলগুলো জরুরি):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;একটাই কোডবেস, একাধিক deploy&lt;/li&gt;
&lt;li&gt;Dependency স্পষ্ট ঘোষণা (&lt;code&gt;.csproj&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;কনফিগ পরিবেশে (environment), কোডে নয়&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Backing service (DB, cache) কে সংযুক্ত রিসোর্স হিসেবে দেখুন&lt;/li&gt;
&lt;li&gt;Build/Release/Run আলাদা&lt;/li&gt;
&lt;li&gt;Process stateless&lt;/li&gt;
&lt;li&gt;দ্রুত চালু ও সুন্দরভাবে বন্ধ (graceful shutdown: &lt;code&gt;IHostApplicationLifetime&lt;/code&gt;, চলমান request শেষ করে বন্ধ)&lt;/li&gt;
&lt;li&gt;লগ event stream হিসেবে stdout এ&lt;/li&gt;
&lt;li&gt;Dev/Staging/Prod যতটা সম্ভব সদৃশ
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// অগ্রাধিকার ক্রমে (পরেরটা আগেরটাকে ওভাররাইড করে)&lt;/span&gt;
&lt;span class="c1"&gt;// appsettings.json → appsettings.{Environment}.json → user-secrets(dev) → Environment variables → Command line&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Options pattern: টাইপ-সেফ কনফিগ&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PaymentOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BindConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateDataAnnotations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateOnStart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                &lt;span class="c1"&gt;// ভুল কনফিগে অ্যাপ চালুই হবে না, রাতে নয়, এখনই ধরা&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Part 9 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Logs + Metrics + Traces: তিনটিই দরকার; OpenTelemetry মানসম্মত পথ।&lt;/li&gt;
&lt;li&gt;Structured logging, correlation ID; alert হবে লক্ষণে, সংখ্যায় কম।&lt;/li&gt;
&lt;li&gt;Docker multi-stage, Compose স্থানীয় পরিবেশে; Kubernetes প্রয়োজন হলে, নইলে ম্যানেজড PaaS।&lt;/li&gt;
&lt;li&gt;CI/CD ও IaC; নিরাপদ deploy (rolling, blue-green, canary, feature flag)।&lt;/li&gt;
&lt;li&gt;Cloud এ Multi-AZ, auto-scaling, খরচ নজরদারি।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 10: Performance, Capacity Planning ও .NET Internals
&lt;/h2&gt;

&lt;p&gt;"সাইট ধীর" শুনলে বেশিরভাগ ডেভেলপার অনুমানে কোড বদলাতে শুরু করে। পেশাদার আগে &lt;strong&gt;মাপে&lt;/strong&gt;। Donald Knuth এর কথা: "অকাল optimization সব অনিষ্টের মূল।" এই Part এ শিখব কীভাবে মেপে, সঠিক জায়গায়, সঠিক উপায়ে দ্রুত করবেন।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১০.১ Performance এর সোনালি নিয়ম
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;আগে মাপুন, তারপর বদলান।&lt;/strong&gt; Profiler ছাড়া অনুমান প্রায়ই ভুল।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bottleneck খুঁজুন।&lt;/strong&gt; সিস্টেমের গতি সবচেয়ে ধীর অংশ ঠিক করে। ১% অংশ ১০ গুণ দ্রুত করলে লাভ নগণ্য।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;বেশিরভাগ সময় বাধা I/O&lt;/strong&gt; (DB, নেটওয়ার্ক), CPU নয়।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;সবচেয়ে সস্তা জয় আগে:&lt;/strong&gt; index, N+1 ঠিক, cache, compression।&lt;/li&gt;
&lt;li&gt;পরিবর্তনের পরে &lt;strong&gt;আবার মাপুন&lt;/strong&gt;, সত্যিই উন্নতি হয়েছে কি না।&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;পারফরম্যান্স সমস্যা খোঁজার ক্রম:&lt;/strong&gt; ইউজার-অভিজ্ঞতা (ব্রাউজার, ছবি, JS) → নেটওয়ার্ক → API/অ্যাপ → ডেটাবেস।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১০.২ async/await: .NET এর সবচেয়ে বড় scalability হাতিয়ার (এবং ফাঁদ)
&lt;/h3&gt;

&lt;p&gt;উপমা: ওয়েটার (thread) এক টেবিলের রান্নার জন্য রান্নাঘরের সামনে দাঁড়িয়ে অপেক্ষা করলে ১০ টেবিল সামলাতে ১০ জন ওয়েটার লাগে। &lt;code&gt;async&lt;/code&gt; মানে ওয়েটার অর্ডার দিয়ে অন্য টেবিলে চলে যায়, খাবার তৈরি হলে ফিরে আসে। কম thread এ বেশি request।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Thread আটকে রাখে (Thread Pool Starvation এর কারণ)&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;               &lt;span class="c1"&gt;// sync I/O&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// .Result: ডেডলক/আটকে থাকার ঝুঁকি&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Thread মুক্ত রাখে&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;নিয়ম:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I/O (DB, HTTP, ফাইল) এ &lt;strong&gt;সবসময় async&lt;/strong&gt;, শেষ পর্যন্ত ("async all the way")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.Result&lt;/code&gt; / &lt;code&gt;.Wait()&lt;/code&gt; / &lt;code&gt;.GetAwaiter().GetResult()&lt;/code&gt; এড়ান&lt;/strong&gt;: Thread Pool Starvation (সব thread আটকে, সাইট জমে যায়)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;async void&lt;/code&gt; শুধু event handler এ; অন্যত্র &lt;code&gt;async Task&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CPU-বাউন্ড ভারী কাজে async এর লাভ নেই (&lt;code&gt;Task.Run&lt;/code&gt; ASP.NET এ সাধারণত অপ্রয়োজনীয়)&lt;/li&gt;
&lt;li&gt;স্বাধীন কাজ সমান্তরালে: &lt;code&gt;await Task.WhenAll(a, b, c)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ValueTask&lt;/code&gt; শুধু প্রমাণিত হট পাথে (প্রায়ই সুবিধা তুচ্ছ)&lt;/li&gt;
&lt;li&gt;লাইব্রেরি কোডে &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; (ASP.NET Core অ্যাপ কোডে দরকার নেই)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১০.৩ Memory ও Garbage Collector (GC)
&lt;/h3&gt;

&lt;p&gt;.NET স্বয়ংক্রিয় memory পরিচালনা করে, কিন্তু অনেক অকারণ allocation GC কে ব্যস্ত রাখে এবং request ধীর করে (GC pause)।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;মূল ধারণা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; ছোট, দ্রুত; value type, local variable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heap:&lt;/strong&gt; বড় অবজেক্ট, GC পরিচালিত&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation 0/1/2:&lt;/strong&gt; নতুন অবজেক্ট Gen0 এ (সস্তা সংগ্রহ); বেঁচে থাকলে উঁচু প্রজন্মে। &lt;strong&gt;Gen2 সংগ্রহ ব্যয়বহুল&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LOH (Large Object Heap):&lt;/strong&gt; ~৮৫,০০০ বাইটের বেশি অবজেক্ট; ফ্র্যাগমেন্টেশন ও Gen2 চাপ বাড়ায়&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ব্যবহারিক কৌশল:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ লুপে string জোড়া: প্রতিবার নতুন string&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ StringBuilder&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ বড় array বারবার না বানিয়ে পুল&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArrayPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ব্যবহার */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ArrayPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Span&amp;lt;T&amp;gt; দিয়ে কপি ছাড়া slicing&lt;/span&gt;
&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HttpClient&lt;/code&gt; প্রতি request এ &lt;code&gt;new&lt;/code&gt; নয়; &lt;code&gt;IHttpClientFactory&lt;/code&gt; (socket exhaustion ও DNS সমস্যা এড়ায়)&lt;/li&gt;
&lt;li&gt;বড় ফাইল/response &lt;strong&gt;stream&lt;/strong&gt; করুন, পুরোটা মেমরিতে নয়&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IDisposable&lt;/code&gt; (DB connection, stream) ঠিকমতো &lt;code&gt;using&lt;/code&gt; দিয়ে ছাড়ুন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Leak এর সাধারণ কারণ:&lt;/strong&gt; static collection এ জমা, event handler unsubscribe না করা, বিশাল cache এ সীমা নেই&lt;/li&gt;
&lt;li&gt;সংগ্রহ ও লুপে LINQ এর অতিরিক্ত অ্যালোকেশন (হট পাথে সচেতন থাকুন, সাধারণ কোডে পাঠযোগ্যতাই আগে)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১০.৪ মাপার টুল
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;টুল&lt;/th&gt;
&lt;th&gt;কাজ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BenchmarkDotNet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;micro-benchmark: কোডের একটা অংশের গতি ও মেমরি নির্ভরযোগ্যভাবে মাপা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;dotnet-counters / dotnet-trace / dotnet-dump&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;চলমান অ্যাপের মেট্রিক, trace, মেমরি dump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visual Studio / JetBrains dotTrace, dotMemory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;profiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application Insights / OpenTelemetry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;production এ ধীর endpoint ও dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQL Profiler / Query Store / EXPLAIN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ধীর query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;k6, JMeter, NBomber, Locust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;load test&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Load Testing এর ধরন:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load test:&lt;/strong&gt; প্রত্যাশিত ট্রাফিকে ঠিক চলে কি না&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stress test:&lt;/strong&gt; সীমা ছাড়িয়ে কোথায় ভাঙে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike test:&lt;/strong&gt; হঠাৎ ট্রাফিক-ঢেউ (ঈদের সেল)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soak test:&lt;/strong&gt; দীর্ঘ সময় (লিক, ক্লান্তি ধরা পড়ে)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;প্রোডাকশন-সদৃশ পরিবেশে ও বাস্তবসম্মত ডেটায়&lt;/strong&gt; পরীক্ষা করুন; খালি ডেটাবেসে ফল প্রতারণাপূর্ণ।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১০.৫ Capacity Planning: Back-of-the-Envelope হিসাব
&lt;/h3&gt;

&lt;p&gt;System design interview ও বাস্তব পরিকল্পনা, দুটোতেই কাজে লাগে। উদ্দেশ্য নিখুঁত সংখ্যা নয়, &lt;strong&gt;মাত্রা (order of magnitude)&lt;/strong&gt; বোঝা।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কাজের সংখ্যা:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;১ দিন ≈ ৮৬,৪০০ সেকেন্ড ≈ ~১ লক্ষ সেকেন্ড (হিসাব সহজ করতে)&lt;/li&gt;
&lt;li&gt;১ কোটি request/দিন ÷ ১ লক্ষ ≈ &lt;strong&gt;১০০ request/সেকেন্ড (গড়)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;পিক সাধারণত গড়ের ২-১০ গুণ → ধরুন ৫ গুণ = &lt;strong&gt;৫০০ RPS&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ShopEasy হিসাব (ধরে নেওয়া সংখ্যা):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily Active Users (DAU): ১০ লক্ষ&lt;/li&gt;
&lt;li&gt;প্রতি ইউজার দিনে ৫০টি request → ৫ কোটি request/দিন&lt;/li&gt;
&lt;li&gt;গড় RPS = ৫ কোটি ÷ ১ লক্ষ ≈ ৫০০; পিক ৫ গুণ ≈ ২,৫০০ RPS&lt;/li&gt;
&lt;li&gt;যদি একটা সার্ভার ~৫০০ RPS সামলায় (মেপে দেখে), তাহলে পিকে ~৫টা + নিরাপদ মার্জিন/redundancy = ৮-১০টা সার্ভার&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Storage হিসাব:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;দিনে ৫০,০০০ অর্ডার × ২ KB = ~১০০ MB/দিন → বছরে ~৩৬ GB। ছোট!&lt;/li&gt;
&lt;li&gt;পণ্য ছবি: ১০ লক্ষ পণ্য × ৫ ছবি × ২০০ KB = ~১ TB। ফাইল স্টোরেজ/CDN এ, DB তে নয়&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read:Write অনুপাত:&lt;/strong&gt; ই-কমার্স সাধারণত read-heavy (১০০:১)। তাই cache ও read replica কার্যকর।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bandwidth:&lt;/strong&gt; ২,৫০০ RPS × ৫০ KB response = ~১২৫ MB/s ≈ ১ Gbps। CDN ও compression তখন জরুরি।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency Budget:&lt;/strong&gt; মোট লক্ষ্য ৩০০ ms হলে ভাগ করুন: নেটওয়ার্ক ৫০, Gateway ১০, API ৭০, DB ১০০, বাকি মার্জিন। কোনো অংশ বাজেট ছাড়ালে বোঝা যায় কোথায় কাজ।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কয়েকটি স্মরণীয় সংখ্যা (আনুমানিক):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;একটা সাধারণ ওয়েব সার্ভার/অ্যাপ instance: শত থেকে কয়েক হাজার RPS (কাজের ওপর নির্ভর)&lt;/li&gt;
&lt;li&gt;একটা ভালো SQL সার্ভার: হাজার থেকে দশ হাজার সাধারণ query/সেকেন্ড&lt;/li&gt;
&lt;li&gt;Redis: প্রতি সেকেন্ডে লক্ষ operation&lt;/li&gt;
&lt;li&gt;Kafka: প্রতি সেকেন্ডে লক্ষ লক্ষ বার্তা (cluster এ)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(এগুলো মোটামুটি ধারণা; আপনার কাজের ধরনে &lt;strong&gt;নিজে মেপে&lt;/strong&gt; নিন।)&lt;/p&gt;

&lt;h3&gt;
  
  
  ১০.৬ Scaling এর ধাপ: ShopEasy এর যাত্রার সারাংশ
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ধাপ&lt;/th&gt;
&lt;th&gt;ইউজার (আনুমানিক)&lt;/th&gt;
&lt;th&gt;কী করবেন&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;১&lt;/td&gt;
&lt;td&gt;~১০০-১,০০০&lt;/td&gt;
&lt;td&gt;একটাই সার্ভার, একটাই DB। সরল ও পর্যাপ্ত।&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;২&lt;/td&gt;
&lt;td&gt;~১০,০০০&lt;/td&gt;
&lt;td&gt;Index, query ঠিক; DB আলাদা সার্ভারে; CDN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;৩&lt;/td&gt;
&lt;td&gt;~১ লক্ষ&lt;/td&gt;
&lt;td&gt;একাধিক app server + Load Balancer (stateless); Redis cache; read replica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;৪&lt;/td&gt;
&lt;td&gt;~১০ লক্ষ&lt;/td&gt;
&lt;td&gt;Message queue, background worker; auto-scaling; DB পারটিশনিং; search ইঞ্জিন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;৫&lt;/td&gt;
&lt;td&gt;১০ লক্ষ+&lt;/td&gt;
&lt;td&gt;Microservices (যদি টিম/ডোমেইন দাবি করে); sharding; multi-region&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;সংখ্যাগুলো কেবল দিকনির্দেশ।&lt;/strong&gt; বাস্তবে ঠিক হয় ট্রাফিক ধরন ও কাজের জটিলতা দিয়ে। কোনো সংখ্যায় লাফ দেওয়ার আগে মাপুন।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability নীতি:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;আগে Stateless বানান&lt;/li&gt;
&lt;li&gt;Database ই সাধারণত প্রথম বাধা; সেখান থেকে শুরু&lt;/li&gt;
&lt;li&gt;পড়া ও লেখা আলাদা করে ভাবুন&lt;/li&gt;
&lt;li&gt;ভারী ও ধীর কাজ অ্যাসিঙ্ক&lt;/li&gt;
&lt;li&gt;সম্ভব হলে কাজ ভাগ করুন (partition), যাতে সমান্তরালে চলে&lt;/li&gt;
&lt;li&gt;স্বয়ংক্রিয় scaling, কিন্তু সীমা ঠিক রাখুন (খরচ ও downstream নির্ভরতা বাঁচাতে)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১০.৭ ফ্রন্টএন্ড-সংলগ্ন ও নেটওয়ার্ক পারফরম্যান্স
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compression:&lt;/strong&gt; &lt;code&gt;app.UseResponseCompression()&lt;/code&gt; (Brotli/Gzip), JSON আকার কমে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/2, HTTP/3:&lt;/strong&gt; একই সংযোগে একাধিক request, Kestrel সমর্থন করে&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static asset:&lt;/strong&gt; CDN, দীর্ঘ cache + ফাইলনামে hash (cache busting), ছবি অপ্টিমাইজ (WebP/AVIF), lazy loading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload ছোট করা:&lt;/strong&gt; শুধু দরকারি ফিল্ড; pagination; &lt;code&gt;System.Text.Json&lt;/code&gt; (Newtonsoft এর চেয়ে সাধারণত দ্রুত); source generator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection Pooling:&lt;/strong&gt; DB connection পুল (ADO.NET ডিফল্টে আছে); পুল ফুরালে request আটকে যায়। &lt;code&gt;Max Pool Size&lt;/code&gt; ও দীর্ঘ-চলা transaction এর দিকে নজর&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep-Alive ও HttpClient পুনর্ব্যবহার&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১০.৮ সাধারণ .NET পারফরম্যান্স ফাঁদ (সংক্ষিপ্ত তালিকা)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ফাঁদ&lt;/th&gt;
&lt;th&gt;সমাধান&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;N+1 query&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Include&lt;/code&gt;/projection/&lt;code&gt;AsSplitQuery&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Select *&lt;/code&gt; ও পুরো entity&lt;/td&gt;
&lt;td&gt;projection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination নেই&lt;/td&gt;
&lt;td&gt;keyset/offset + সীমা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.Result&lt;/code&gt;/&lt;code&gt;.Wait()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;async all the way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;new HttpClient()&lt;/code&gt; প্রতিবার&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IHttpClientFactory&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;বড় object cache এ সীমাহীন&lt;/td&gt;
&lt;td&gt;সাইজ সীমা, মেয়াদ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;লুপের ভেতরে DB/HTTP call&lt;/td&gt;
&lt;td&gt;batch করুন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;অতিরিক্ত লগ&lt;/td&gt;
&lt;td&gt;লেভেল ঠিক রাখুন&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception দিয়ে স্বাভাবিক flow&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TryParse&lt;/code&gt;, ফলাফল-টাইপ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex অকার্যকর (ReDoS)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RegexOptions.Compiled&lt;/code&gt;/source generator, timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reflection/dynamic হট পাথে&lt;/td&gt;
&lt;td&gt;source generator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Part 10 এর সারসংক্ষেপ
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;আগে মাপুন (profiler, load test, trace), তারপর optimize।&lt;/li&gt;
&lt;li&gt;I/O তে async all the way; &lt;code&gt;.Result&lt;/code&gt; নয়।&lt;/li&gt;
&lt;li&gt;অকারণ allocation কমান; &lt;code&gt;IHttpClientFactory&lt;/code&gt;, stream, পুল।&lt;/li&gt;
&lt;li&gt;Capacity planning এ order of magnitude; DAU → RPS → সার্ভার/স্টোরেজ।&lt;/li&gt;
&lt;li&gt;Scale করুন ধাপে ধাপে; প্রয়োজনের আগে নয়।&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 11: Case Study: তিনটি সম্পূর্ণ System Design
&lt;/h2&gt;

&lt;p&gt;তত্ত্ব শিখলেন; এখন প্রয়োগ। প্রতিটি case study তে একই কাঠামো অনুসরণ করব (Part 12 এর ফ্রেমওয়ার্ক): প্রয়োজন → হিসাব → উচ্চস্তরের নকশা → ডেটা মডেল → গভীরে সিদ্ধান্ত → ট্রেড-অফ।&lt;/p&gt;

&lt;h3&gt;
  
  
  Case Study ১: URL Shortener (যেমন bit.ly)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; লম্বা URL কে ছোট করে (&lt;code&gt;sho.rt/aB3xK9&lt;/code&gt;), ছোটটায় ঢুকলে মূলটায় redirect।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ১: প্রয়োজন
&lt;/h4&gt;

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

&lt;ul&gt;
&lt;li&gt;লম্বা URL দিলে ছোট URL তৈরি&lt;/li&gt;
&lt;li&gt;ছোট URL এ ঢুকলে redirect&lt;/li&gt;
&lt;li&gt;(ঐচ্ছিক) কাস্টম alias, মেয়াদ, ক্লিক-অ্যানালিটিক্স&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Non-functional:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redirect অত্যন্ত দ্রুত (&amp;lt; ১০০ ms) ও উচ্চ availability&lt;/li&gt;
&lt;li&gt;Read-heavy: প্রতি ১ তৈরিতে ~১০০ redirect&lt;/li&gt;
&lt;li&gt;Short code অনুমানযোগ্য নয়, ডুপ্লিকেট নয়&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ধাপ ২: হিসাব (ধরে নিচ্ছি)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;নতুন URL: ১০ কোটি/মাস ≈ ~৪০ write/সেকেন্ড&lt;/li&gt;
&lt;li&gt;Redirect (১০০:১): ~৪,০০০ read/সেকেন্ড গড়; পিক ~১২,০০০&lt;/li&gt;
&lt;li&gt;৫ বছর সংরক্ষণ: ১০ কোটি × ১২ × ৫ = ৬০০ কোটি রেকর্ড। প্রতিটি ~৫০০ বাইট → &lt;strong&gt;~৩ TB&lt;/strong&gt;। (একটা আধুনিক ক্লাস্টারে সম্ভব)&lt;/li&gt;
&lt;li&gt;Short code দৈর্ঘ্য: Base62 (&lt;code&gt;a-z, A-Z, 0-9&lt;/code&gt;) এ ৭ অক্ষর = 62^7 ≈ ৩.৫ ট্রিলিয়ন সংমিশ্রণ। ৬০০ কোটির জন্য যথেষ্ট&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ধাপ ৩: উচ্চস্তরের নকশা
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → CDN/Load Balancer → API Servers (stateless, .NET)
                                 ├── Cache (Redis): code → longUrl
                                 └── Database (code → longUrl)
                                 └── (async) Queue → Analytics Worker → Analytics DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ধাপ ৪: API
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/urls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateUrlRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IUrlService&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LongUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomAlias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;shortUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://sho.rt/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{code}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IUrlService&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ResolveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;301 বনাম 302:&lt;/strong&gt; ৩০১ (স্থায়ী) ব্রাউজার cache করে, ফলে সার্ভারে hit কমে কিন্তু ক্লিক গণনা হারায়। অ্যানালিটিক্স চাইলে ৩০২।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৫: Short code তৈরির কৌশল (মূল সিদ্ধান্ত)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;পদ্ধতি&lt;/th&gt;
&lt;th&gt;কীভাবে&lt;/th&gt;
&lt;th&gt;সমস্যা&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hash (MD5/SHA) এর প্রথম ৭ অক্ষর&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;URL এর hash&lt;/td&gt;
&lt;td&gt;সংঘর্ষ (collision) সম্ভব; পরীক্ষা ও retry লাগে&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Random Base62&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;র‍্যান্ডম ৭ অক্ষর&lt;/td&gt;
&lt;td&gt;সংঘর্ষ সম্ভব (unique constraint + retry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Counter + Base62&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;একটা বাড়তে থাকা সংখ্যাকে Base62 এ রূপান্তর&lt;/td&gt;
&lt;td&gt;সংঘর্ষ নেই; কিন্তু অনুমানযোগ্য, আর কাউন্টার distributed এ জটিল&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pre-generated Key Service (KGS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;আগে থেকে অনেক unique key তৈরি করে রাখা; চাইলে একটা দেওয়া&lt;/td&gt;
&lt;td&gt;সরল ও দ্রুত; KGS এর availability নিশ্চিত করতে হবে&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;সিদ্ধান্ত:&lt;/strong&gt; Counter-based ID (Snowflake-জাতীয় বা ডেটাবেস sequence ব্লক বরাদ্দ করে প্রতি সার্ভারে) + Base62 এনকোড। অনুমানযোগ্যতা সমস্যা হলে সংখ্যাকে অদলবদল (permutation/obfuscation) করুন। প্রতিটি API সার্ভার একসাথে ১০০০ ID এর ব্লক নিয়ে স্থানীয়ভাবে বিতরণ করে, প্রতিবার DB তে না গিয়ে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base62&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Chars&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Chars&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;/=&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ধাপ ৬: ডেটা মডেল ও ডেটাবেস
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Urls(Code PK, LongUrl, UserId, CreatedAt, ExpiresAt)&lt;/code&gt;। সরল key-value access pattern (শুধু code দিয়ে খোঁজা), জটিল JOIN নেই। তাই SQL (PostgreSQL) বা NoSQL (DynamoDB/Cassandra) দুটোই চলে। শুরুতে SQL; বিপুল scale এ key-value store।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৭: Scaling ও অপ্টিমাইজেশন
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache (Redis):&lt;/strong&gt; জনপ্রিয় URL সামান্য সংখ্যক; ২০% URL এ ৮০% ট্রাফিক (Pareto)। Cache-Aside, LRU eviction, TTL। ফলে DB তে খুব কম hit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read replica&lt;/strong&gt;, তারপর &lt;strong&gt;sharding&lt;/strong&gt; (shard key = code এর hash)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics অ্যাসিঙ্ক:&lt;/strong&gt; ক্লিক event queue তে ফেলে আলাদা worker; redirect এর পথ ধীর হয় না&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;মেয়াদোত্তীর্ণ লিংক পরিষ্কার:&lt;/strong&gt; background job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limit ও অপব্যবহার প্রতিরোধ:&lt;/strong&gt; ম্যালওয়্যার/ফিশিং লিংক চেক, তৈরির rate limit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ট্রেড-অফ:&lt;/strong&gt; Cache এ মুছে ফেলা/বদলানো লিংকের পুরনো ডেটা কিছুক্ষণ থাকতে পারে (TTL ও invalidation)। Redirect এ eventual consistency গ্রহণযোগ্য।&lt;/p&gt;




&lt;h3&gt;
  
  
  Case Study ২: E-Commerce Order System (ShopEasy এর কেন্দ্র)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; ঈদের সেলে হাজারো ইউজার একসাথে কিনছে; স্টক ঠিক রাখা, পেমেন্ট নিরাপদ, কোনো অর্ডার হারানো বা দুবার প্রক্রিয়া নয়।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ১: প্রয়োজন
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;পণ্য ব্রাউজ/সার্চ, cart, checkout, পেমেন্ট, অর্ডার ট্র্যাকিং, ইমেইল/SMS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;কঠোর সঠিকতা:&lt;/strong&gt; স্টকের বেশি বিক্রি নয়, পেমেন্ট ডুপ্লিকেট নয়&lt;/li&gt;
&lt;li&gt;ঈদের সময় ১০-২০ গুণ spike; ৯৯.৯% availability (checkout এ)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ধাপ ২: Bounded Context (Part 3 এর DDD)
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Catalog&lt;/code&gt; (পণ্য), &lt;code&gt;Cart&lt;/code&gt;, &lt;code&gt;Ordering&lt;/code&gt;, &lt;code&gt;Inventory&lt;/code&gt;, &lt;code&gt;Payment&lt;/code&gt;, &lt;code&gt;Shipping&lt;/code&gt;, &lt;code&gt;Notification&lt;/code&gt;, &lt;code&gt;Identity&lt;/code&gt;।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;স্থাপত্য সিদ্ধান্ত:&lt;/strong&gt; শুরুতে &lt;strong&gt;Modular Monolith&lt;/strong&gt;। চাপের অংশ (Catalog পড়া, Payment) পরে আলাদা করা যাবে।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৩: উচ্চস্তরের প্রবাহ
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → CDN → API Gateway (YARP: auth, rate limit)
   → Catalog (read-heavy: Redis + Elasticsearch)
   → Cart (Redis, user-keyed)
   → Ordering → [Payment, Inventory, Shipping] (Saga)
   → Outbox → Message Broker → Notification, Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ধাপ ৪: Catalog (পড়া-ভারী)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;মূল ডেটা SQL এ; সার্চ ও ফিল্টারের জন্য &lt;strong&gt;Elasticsearch&lt;/strong&gt; (পণ্য ইভেন্টে সিঙ্ক, eventual consistency)&lt;/li&gt;
&lt;li&gt;ছবি &lt;strong&gt;CDN&lt;/strong&gt; এ; পণ্য পেজ HybridCache&lt;/li&gt;
&lt;li&gt;দাম ও স্টক সামান্য পুরনো দেখালেও চলে, কিন্তু &lt;strong&gt;checkout এ আবার যাচাই&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ধাপ ৫: Cart
&lt;/h4&gt;

&lt;p&gt;স্থায়ী নয়, দ্রুত বদলায়, ইউজার-কেন্দ্রিক। &lt;strong&gt;Redis Hash&lt;/strong&gt; (&lt;code&gt;cart:{userId}&lt;/code&gt; → পণ্য ও পরিমাণ), মেয়াদ ৭ দিন। লগইনের আগে অতিথির cart কে (cookie) লগইনের পর মার্জ।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৬: Checkout ও Inventory: কঠিনতম অংশ
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; শেষ ১০টা ফোন, ১০,০০০ জন একসাথে কিনতে চায়।&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;কৌশল: Reservation (সাময়িক সংরক্ষণ)।&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checkout এ স্টক &lt;strong&gt;Reserve&lt;/strong&gt; (atomic, শর্তসাপেক্ষ আপডেট), মেয়াদ ১৫ মিনিট&lt;/li&gt;
&lt;li&gt;পেমেন্ট সফল হলে Reservation &lt;strong&gt;নিশ্চিত&lt;/strong&gt;; ব্যর্থ বা মেয়াদোত্তীর্ণ হলে স্টক &lt;strong&gt;ফেরত&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Atomic: যথেষ্ট স্টক থাকলে তবেই কমাও (race condition নেই)&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteSqlInterpolatedAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$@"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    UPDATE Inventory&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    SET Available = Available - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Reserved = Reserved + &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;    WHERE ProductId = &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; AND Available &amp;gt;= &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Out of stock"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;অতি-হট পণ্যে (ফ্ল্যাশ সেল) এক row এ হাজারো লেখা &lt;strong&gt;hot row lock contention&lt;/strong&gt; ঘটায়। সমাধান: স্টক কয়েকটা "বালতি" (slot) এ ভাগ করা, Redis এ atomic &lt;code&gt;DECR&lt;/code&gt; দিয়ে দ্রুত গেট, ও পরে DB তে সামঞ্জস্য; এবং waiting room/queue দিয়ে ট্রাফিক নিয়ন্ত্রণ।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৭: Order Saga (Part 6)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;স্বাভাবিক প্রবাহ:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated(Pending)
 → ReserveStock ✔
 → ChargePayment ✔
 → OrderConfirmed → ShipOrder → Notify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ব্যর্থতা সামলানো:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;যেখানে ব্যর্থ&lt;/th&gt;
&lt;th&gt;ক্ষতিপূরণ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stock নেই&lt;/td&gt;
&lt;td&gt;Order বাতিল (কিছু ফেরত দিতে হবে না)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment ব্যর্থ&lt;/td&gt;
&lt;td&gt;স্টক Release, Order বাতিল&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment সফল কিন্তু Shipping ব্যর্থ&lt;/td&gt;
&lt;td&gt;পুনরায় চেষ্টা; শেষে Refund + স্টক Release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment gateway timeout (সফল না ব্যর্থ অজানা)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;অবস্থা অজানা&lt;/strong&gt;: gateway এ status query/webhook এর অপেক্ষা; স্বয়ংক্রিয়ভাবে পুনর্মিলন (reconciliation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Orchestration&lt;/strong&gt; পছন্দ (MassTransit State Machine বা Durable Functions): প্রবাহ স্পষ্ট, সহজে ট্র্যাক ও debug।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ৮: Payment নিরাপত্তা ও সঠিকতা
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;কার্ডের তথ্য নিজে ধরবেন না; gateway এর hosted page/tokenization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency Key&lt;/strong&gt; (Part 5) প্রতিটি পেমেন্ট চেষ্টায়&lt;/li&gt;
&lt;li&gt;gateway থেকে &lt;strong&gt;Webhook&lt;/strong&gt; এর মাধ্যমে চূড়ান্ত অবস্থা (signature যাচাই, idempotent handler)&lt;/li&gt;
&lt;li&gt;নিয়মিত &lt;strong&gt;Reconciliation job&lt;/strong&gt; (দৈনিক gateway রিপোর্টের সাথে আমাদের রেকর্ড মেলানো)&lt;/li&gt;
&lt;li&gt;Money এর জন্য &lt;code&gt;decimal&lt;/code&gt;/Money value object, একক মুদ্রা, পূর্ণসংখ্যায় (পয়সা) সংরক্ষণ বিবেচনা&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ধাপ ৯: Order এর অবস্থা (State Machine)
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Pending → PaymentPending → Paid → Shipped → Delivered&lt;/code&gt;; বিকল্প &lt;code&gt;Cancelled&lt;/code&gt;, &lt;code&gt;Refunded&lt;/code&gt;। কেবল &lt;strong&gt;বৈধ transition&lt;/strong&gt; অনুমোদিত (Domain model এ, &lt;code&gt;Order.Confirm()&lt;/code&gt; ইত্যাদিতে), অবৈধ ধাপে exception।&lt;/p&gt;

&lt;h4&gt;
  
  
  ধাপ ১০: Spike সামলানো (ঈদের সেল)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;আগে থেকে &lt;strong&gt;scale out&lt;/strong&gt; (নির্ধারিত সময়ে auto-scaling) ও cache গরম (warm-up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Waiting Room:&lt;/strong&gt; ট্রাফিক সীমা ছাড়ালে ইউজারকে লাইনে রাখা, সিস্টেম বাঁচানো&lt;/li&gt;
&lt;li&gt;Checkout ছাড়া অনাবশ্যক ফিচার সাময়িক বন্ধ (graceful degradation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt;, বট প্রতিরোধ (CAPTCHA/WAF)&lt;/li&gt;
&lt;li&gt;Load test আগে (Spike test)&lt;/li&gt;
&lt;li&gt;DB connection pool ও queue গভীরতা পর্যবেক্ষণ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ট্রেড-অফ:&lt;/strong&gt; Strong consistency (স্টক, পেমেন্ট) ধীর কিন্তু প্রয়োজনীয়; Catalog, সার্চ, রিভিউ eventual consistency তে স্বচ্ছন্দ। &lt;strong&gt;একই সিস্টেমে বিভিন্ন অংশে ভিন্ন consistency&lt;/strong&gt;; এটাই পরিপক্ক design।&lt;/p&gt;




&lt;h3&gt;
  
  
  Case Study ৩: Notification System (ইমেইল, SMS, Push)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;সমস্যা:&lt;/strong&gt; অর্ডার, OTP, প্রমোশন: কোটি কোটি বার্তা, বিভিন্ন চ্যানেলে, নির্ভরযোগ্যভাবে, ডুপ্লিকেট ছাড়া।&lt;/p&gt;

&lt;h4&gt;
  
  
  প্রয়োজন
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;চ্যানেল: Email, SMS, Push, In-app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OTP&lt;/strong&gt;: সেকেন্ডে পৌঁছানো (উচ্চ অগ্রাধিকার); &lt;strong&gt;প্রমোশন&lt;/strong&gt;: ধীরে চললেও চলে&lt;/li&gt;
&lt;li&gt;ইউজারের পছন্দ ও opt-out সম্মান করা&lt;/li&gt;
&lt;li&gt;ডুপ্লিকেট নয়, বার্তা হারানো নয়&lt;/li&gt;
&lt;li&gt;Third-party (Twilio, SendGrid ইত্যাদি) ধীর বা ডাউন হতে পারে&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  নকশা
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;সার্ভিস (Order, Auth...) → Notification API
     → Validation + Preference check + Template
     → Outbox → Message Broker
          ├── high-priority queue  (OTP, লেনদেন)
          ├── normal queue         (অর্ডার আপডেট)
          └── bulk queue           (প্রমোশন)
     → Channel Workers (Email / SMS / Push), প্রতিটি আলাদাভাবে scalable
     → Provider Adapter (Strategy: SendGrid | SES | Twilio ...)
     → Delivery status (webhook) → Notification DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  মূল সিদ্ধান্ত
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;অগ্রাধিকার-ভিত্তিক আলাদা queue:&lt;/strong&gt; প্রমোশনের বিশাল ব্যাচ OTP আটকাবে না (&lt;strong&gt;Noisy Neighbor&lt;/strong&gt; প্রতিরোধ, Bulkhead)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider abstraction (Strategy/Adapter):&lt;/strong&gt; &lt;code&gt;IEmailProvider&lt;/code&gt; এর পেছনে একাধিক ভেন্ডর; একটা ডাউন হলে &lt;strong&gt;Fallback&lt;/strong&gt; অন্যটিতে (Circuit Breaker সহ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency:&lt;/strong&gt; প্রতিটি বার্তার একটা unique key; consumer ডুপ্লিকেট প্রত্যাখ্যান করে (ইউজার একই OTP দুবার পাবে না)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry + backoff + DLQ:&lt;/strong&gt; ব্যর্থ বার্তা পুনরায়, শেষে DLQ এ ও alert&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting (provider ও ইউজার-ভিত্তিক):&lt;/strong&gt; provider এর সীমা মানা; এক ইউজারকে স্প্যাম নয়&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template ও লোকালাইজেশন:&lt;/strong&gt; বাংলা/ইংরেজি; ভেরিয়েবল প্রতিস্থাপন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled/Delayed বার্তা:&lt;/strong&gt; "সকাল ৯টায় পাঠান" (delayed queue / scheduler)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ট্র্যাকিং:&lt;/strong&gt; Sent → Delivered → Opened/Failed webhook থেকে অবস্থা হালনাগাদ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;অ্যানালিটিক্স অ্যাসিঙ্ক:&lt;/strong&gt; মূল পথ ধীর নয়
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISmsProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SendResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FailoverSmsSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ISmsProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FailoverSmsSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SendResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{Provider} failed, trying next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotificationFailedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All providers failed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ট্রেড-অফ:&lt;/strong&gt; exactly-once অসম্ভব, তাই At-least-once + idempotency। অত্যন্ত জরুরি OTP তে সামান্য ডুপ্লিকেট চেয়ে হারানো খারাপ, তাই ডুপ্লিকেট সহনীয়, হারানো নয়।&lt;/p&gt;




&lt;h3&gt;
  
  
  কেস স্টাডি থেকে শিক্ষা (সূত্র)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;প্রয়োজন ও সংখ্যা দিয়ে শুরু&lt;/strong&gt; (অনুমান নয়)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-heavy&lt;/strong&gt; হলে cache ও replica; &lt;strong&gt;Write-heavy&lt;/strong&gt; হলে queue, partition&lt;/li&gt;
&lt;li&gt;ধীর, ঝুঁকিপূর্ণ বা তৃতীয়-পক্ষ নির্ভর কাজ &lt;strong&gt;অ্যাসিঙ্ক&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;সঠিকতা-সংবেদনশীল অংশে (টাকা, স্টক) &lt;strong&gt;strong consistency ও idempotency&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;প্রতিটি নির্ভরতার জন্য ব্যর্থতার পরিকল্পনা (timeout, retry, fallback)&lt;/li&gt;
&lt;li&gt;সরল দিয়ে শুরু, মাপার পর জটিল করুন&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 12: বাস্তব জীবনের পদ্ধতি: Framework, Trade-off ও Roadmap
&lt;/h2&gt;

&lt;p&gt;এতক্ষণ আপনি অনেক "হাতিয়ার" শিখলেন। এখন শেখার সময় &lt;strong&gt;কখন কোনটা ধরবেন&lt;/strong&gt; এবং &lt;strong&gt;কীভাবে সিদ্ধান্ত নেবেন ও ব্যাখ্যা করবেন।&lt;/strong&gt; এটাই একজন সিনিয়র/আর্কিটেক্টকে আলাদা করে।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১২.১ System Design এর ৭-ধাপ ফ্রেমওয়ার্ক
&lt;/h3&gt;

&lt;p&gt;যেকোনো নতুন সিস্টেম (প্রজেক্টে বা ইন্টারভিউতে) এই ক্রমে ভাবুন:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;প্রয়োজন স্পষ্ট করুন (Requirements)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Functional: সিস্টেম কী করবে?&lt;/li&gt;
&lt;li&gt;Non-functional: কতজন ইউজার? কত দ্রুত? কতটা নির্ভরযোগ্য? নিরাপত্তা? বাজেট? সময়সীমা?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;প্রশ্ন করুন।&lt;/strong&gt; অস্পষ্টতা নিয়ে নকশা শুরু করবেন না।&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;সীমা ও অনুমান (Constraints &amp;amp; Assumptions):&lt;/strong&gt; টিমের আকার ও দক্ষতা, বাজেট, বিদ্যমান সিস্টেম, আইনি বিধি।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;হিসাব (Estimation):&lt;/strong&gt; DAU, RPS, স্টোরেজ, bandwidth (Part 10)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;উচ্চস্তরের নকশা (High-level Design):&lt;/strong&gt; প্রধান উপাদান (ক্লায়েন্ট, Gateway, সার্ভিস, DB, Cache, Queue) ও তাদের সংযোগ। আগে সরল নকশা।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ডেটা মডেল ও API:&lt;/strong&gt; মূল entity, সম্পর্ক, access pattern, প্রধান endpoint।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;গভীরে যাওয়া (Deep Dive):&lt;/strong&gt; সবচেয়ে কঠিন/ঝুঁকিপূর্ণ অংশ (স্টক রেস, ID তৈরি, ডুপ্লিকেট প্রতিরোধ)।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bottleneck, ব্যর্থতা ও Trade-off:&lt;/strong&gt; কোথায় ভাঙবে? কীভাবে scale হবে? কী ছাড়লাম, কেন?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;সময় ভাগ (ইন্টারভিউয়ে ৪৫ মিনিট):&lt;/strong&gt; প্রয়োজন ৫-৮, উচ্চস্তর ১০-১৫, গভীরে ১৫-২০, শেষে ৫।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১২.২ সিদ্ধান্ত নেওয়ার প্রশ্নমালা
&lt;/h3&gt;

&lt;p&gt;যেকোনো প্রযুক্তি/কৌশল বাছাইয়ের আগে নিজেকে জিজ্ঞেস করুন:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;কোন সমস্যা সমাধান করছি?&lt;/strong&gt; (সমস্যা প্রমাণিত, নাকি কল্পিত?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;সরলতম বিকল্প কী?&lt;/strong&gt; সেটা কেন যথেষ্ট নয়?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;এর দাম কী?&lt;/strong&gt; (জটিলতা, শেখার সময়, অপারেশন, খরচ, ভেন্ডর-নির্ভরতা)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;টিম কি এটা চালাতে পারবে?&lt;/strong&gt; রাত ২টায় কে ঠিক করবে?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;উল্টানো কত কঠিন?&lt;/strong&gt; (Two-way door বনাম one-way door সিদ্ধান্ত। সহজে ফেরানো যায় এমনটা দ্রুত নিন, ফেরানো কঠিনটা সাবধানে)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;বিকল্প কী কী ছিল, কেন বাদ দিলাম?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১২.৩ Architecture Decision Record (ADR)
&lt;/h3&gt;

&lt;p&gt;ছয় মাস পরে কেউ জিজ্ঞেস করবে: "কেন আমরা MongoDB নিলাম না?" কেউ মনে রাখবে না। তাই সিদ্ধান্ত &lt;strong&gt;লিখে রাখুন&lt;/strong&gt;: ছোট একটা ফাইল (&lt;code&gt;docs/adr/0007-use-postgresql.md&lt;/code&gt;), Git এ কোডের সাথে।&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# ADR-0007: অর্ডার ডেটাবেস হিসেবে PostgreSQL&lt;/span&gt;

&lt;span class="gu"&gt;## অবস্থা&lt;/span&gt;
গৃহীত (২০২৬-১০-২৮)

&lt;span class="gu"&gt;## প্রেক্ষাপট&lt;/span&gt;
অর্ডার ও পেমেন্টে শক্তিশালী ACID transaction ও জটিল রিপোর্টিং লাগবে। টিম SQL এ দক্ষ।

&lt;span class="gu"&gt;## সিদ্ধান্ত&lt;/span&gt;
PostgreSQL ব্যবহার করব (ম্যানেজড সার্ভিসে)।

&lt;span class="gu"&gt;## বিকল্প&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; MongoDB: নমনীয় schema, কিন্তু multi-document transaction ও রিপোর্টিং জটিল
&lt;span class="p"&gt;-&lt;/span&gt; SQL Server: ভালো, কিন্তু লাইসেন্স খরচ বেশি

&lt;span class="gu"&gt;## পরিণতি&lt;/span&gt;
&lt;span class="p"&gt;+&lt;/span&gt; ACID, পরিপক্ক ইকোসিস্টেম, কম খরচ
− অতি-বিশাল scale এ sharding জটিল (বর্তমান প্রয়োজনে প্রযোজ্য নয়)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;সিদ্ধান্ত বদলালে পুরনোটা মুছবেন না; "Superseded by ADR-0015" চিহ্নিত করুন। ইতিহাসই শিক্ষা।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১২.৪ বিখ্যাত ট্রেড-অফের সারণি (স্মরণীয়)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ট্রেড-অফ&lt;/th&gt;
&lt;th&gt;একদিকে&lt;/th&gt;
&lt;th&gt;অন্যদিকে&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consistency বনাম Availability&lt;/td&gt;
&lt;td&gt;সঠিক কিন্তু মাঝে মাঝে অনুপলব্ধ&lt;/td&gt;
&lt;td&gt;সবসময় চালু কিন্তু সাময়িক পুরনো&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency বনাম Throughput&lt;/td&gt;
&lt;td&gt;প্রতিটি দ্রুত&lt;/td&gt;
&lt;td&gt;মোট বেশি কাজ (batching)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Normalize বনাম Denormalize&lt;/td&gt;
&lt;td&gt;সঙ্গত ডেটা&lt;/td&gt;
&lt;td&gt;দ্রুত পড়া&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monolith বনাম Microservices&lt;/td&gt;
&lt;td&gt;সরল&lt;/td&gt;
&lt;td&gt;স্বাধীন scaling/টিম&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync বনাম Async&lt;/td&gt;
&lt;td&gt;সরল ও তাৎক্ষণিক উত্তর&lt;/td&gt;
&lt;td&gt;সহনশীল ও scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build বনাম Buy&lt;/td&gt;
&lt;td&gt;সম্পূর্ণ নিয়ন্ত্রণ&lt;/td&gt;
&lt;td&gt;দ্রুত ও কম রক্ষণাবেক্ষণ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL বনাম NoSQL&lt;/td&gt;
&lt;td&gt;সম্পর্ক ও ACID&lt;/td&gt;
&lt;td&gt;নমনীয়তা ও বিপুল scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;গতি&lt;/td&gt;
&lt;td&gt;পুরনো ডেটার ঝুঁকি&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index&lt;/td&gt;
&lt;td&gt;দ্রুত পড়া&lt;/td&gt;
&lt;td&gt;ধীর লেখা ও বেশি জায়গা&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong বনাম Eventual consistency&lt;/td&gt;
&lt;td&gt;সহজ যুক্তি&lt;/td&gt;
&lt;td&gt;scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ১২.৫ সাধারণ ভুল (যা সবাই করে)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;শুরুতেই Microservices&lt;/strong&gt;: বিতরিত জটিলতা কিন্তু সুবিধা নেই (Distributed Monolith)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resume-Driven Development&lt;/strong&gt;: প্রযুক্তি বাছা কারণ "CV তে ভালো দেখাবে", প্রয়োজন নয়&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;অকাল অপ্টিমাইজেশন ও অতিরিক্ত নকশা (Over-engineering)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;বিপরীতে:&lt;/strong&gt; একেবারে নকশাহীন "Big Ball of Mud"&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;মাপার আগেই scaling-এর চেষ্টা&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ব্যর্থতা ভুলে যাওয়া&lt;/strong&gt;: Timeout, retry, idempotency নেই&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Database&lt;/strong&gt; একাধিক সার্ভিসের মধ্যে (লুকানো coupling)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability পরে ভাবা&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;নিরাপত্তা শেষে যোগ করা&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Golden Hammer&lt;/strong&gt;: একটা প্রিয় টুল সবখানে ("যার হাতে হাতুড়ি, সবই পেরেক")&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ডকুমেন্টেশন ও সিদ্ধান্ত না লেখা&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;অপারেশনাল খরচ উপেক্ষা&lt;/strong&gt;: কে মনিটর করবে, কে on-call?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;টিমের সক্ষমতা বিবেচনা না করা&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache যোগ করে ডেটা সঠিকতার কথা ভুলে যাওয়া&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big-bang rewrite&lt;/strong&gt;: Strangler Fig ই নিরাপদ&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ১২.৬ Testing কৌশল (আর্কিটেকচারের অঙ্গ)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test Pyramid:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit test (বেশি, দ্রুত):&lt;/strong&gt; একক ক্লাস/লজিক। xUnit/NUnit + Moq/NSubstitute। Domain logic এখানে সবচেয়ে ভালো (Clean Architecture এর সুবিধা)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration test (মাঝারি):&lt;/strong&gt; আসল DB, queue সহ। &lt;strong&gt;Testcontainers&lt;/strong&gt; দিয়ে Docker এ সত্যিকারের SQL/Redis/RabbitMQ চালিয়ে। ASP.NET Core &lt;code&gt;WebApplicationFactory&lt;/code&gt; দিয়ে পুরো API পরীক্ষা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract test (মাইক্রোসার্ভিসে):&lt;/strong&gt; সার্ভিসগুলোর চুক্তি মানা হচ্ছে কি না (Pact)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-End (কম, ধীর):&lt;/strong&gt; মূল ব্যবহারকারী যাত্রা&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture test:&lt;/strong&gt; &lt;code&gt;NetArchTest&lt;/code&gt;/&lt;code&gt;ArchUnitNET&lt;/code&gt; দিয়ে নিয়ম প্রয়োগ (যেমন "Domain কখনো Infrastructure ব্যবহার করবে না")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load/Performance, Security, Chaos test&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersApiTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WebApplicationFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IClassFixture&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WebApplicationFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;GetOrder_WithoutAuth_Returns401&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/orders/1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;টেস্ট নীতি:&lt;/strong&gt; আচরণ পরীক্ষা করুন, বাস্তবায়ন নয়; mock সীমিত রাখুন (ডেটাবেস mock করার চেয়ে Testcontainers প্রায়ই বেশি আস্থা দেয়)।&lt;/p&gt;

&lt;h3&gt;
  
  
  ১২.৭ টেকনিক্যাল ঋণ (Technical Debt) ও Refactoring
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Technical debt:&lt;/strong&gt; আজকের শর্টকাট, কাল সুদসহ শোধ। সবসময় খারাপ নয় (ইচ্ছাকৃত, সচেতন ঋণ মাঝে মাঝে বুদ্ধিমানের কাজ), তবে &lt;strong&gt;দৃশ্যমান রাখুন ও পরিশোধের পরিকল্পনা করুন।&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ঋণের তালিকা রাখুন; প্রতি স্প্রিন্টে কিছু অংশ পরিশোধ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boy Scout Rule:&lt;/strong&gt; কোড যেমন পেয়েছেন তার চেয়ে একটু ভালো রেখে যান&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor নিরাপদে:&lt;/strong&gt; আগে টেস্ট, ছোট ধাপ, আচরণ অপরিবর্তিত&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strangler Fig&lt;/strong&gt; বড় পরিবর্তনে&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১২.৮ আর্কিটেক্ট হিসেবে নরম দক্ষতা
&lt;/h3&gt;

&lt;p&gt;প্রযুক্তি অর্ধেক; বাকি অর্ধেক মানুষ।&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;যোগাযোগ:&lt;/strong&gt; জটিল ধারণা সহজে বোঝান (ডায়াগ্রাম আঁকুন, উপমা দিন)। &lt;strong&gt;C4 Model&lt;/strong&gt; (Context, Container, Component, Code) স্তরে স্তরে ডায়াগ্রামের ভালো পদ্ধতি&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ব্যবসা বুঝুন:&lt;/strong&gt; প্রযুক্তি ব্যবসার সেবক। "এতে ব্যবসার কী লাভ?" বলতে পারুন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ঐক্যমত তৈরি:&lt;/strong&gt; আদেশ নয়, বুঝিয়ে সম্মতি; RFC/ADR প্রক্রিয়া&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;শুনুন ও শিখুন:&lt;/strong&gt; যিনি প্রতিদিন কোড লেখেন, তাঁর ব্যথা বুঝুন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"জানি না" বলতে শিখুন&lt;/strong&gt;, তারপর খুঁজে বের করুন&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;টিমকে এগিয়ে নিন:&lt;/strong&gt; মেন্টরিং, কোড রিভিউ&lt;/li&gt;
&lt;li&gt;আর্কিটেক্ট হাতে-কলমে কোডও লেখে; আইভরি টাওয়ারে নয়&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ১২.৯ আপনার শেখার Roadmap (Beginner → Advanced)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;পর্যায় ১: ভিত্তি (১-২ মাস)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] C# ও ASP.NET Core ভালোভাবে; DI, Middleware, Configuration&lt;/li&gt;
&lt;li&gt;[ ] HTTP, REST, SQL ও EF Core, index&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] SOLID ও কয়েকটি design pattern&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;প্রজেক্ট:&lt;/strong&gt; একটা Layered CRUD API (ShopEasy v1)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;পর্যায় ২: মাঝারি (২-৪ মাস)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Clean Architecture/Vertical Slice, CQRS&lt;/li&gt;
&lt;li&gt;[ ] Authentication (JWT/OIDC), Authorization&lt;/li&gt;
&lt;li&gt;[ ] Caching (Redis), Docker, CI/CD&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] Logging/Serilog, Unit + Integration test&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;প্রজেক্ট:&lt;/strong&gt; ShopEasy v2 (Clean Architecture, Docker, CI, auth, cache)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;পর্যায় ৩: উন্নত (৪-৮ মাস)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Messaging (RabbitMQ + MassTransit), Outbox, Saga&lt;/li&gt;
&lt;li&gt;[ ] Resilience (Polly), OpenTelemetry, Health checks&lt;/li&gt;
&lt;li&gt;[ ] DDD, Modular Monolith, তারপর Microservices&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] Kubernetes/ক্লাউড (Azure/AWS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;প্রজেক্ট:&lt;/strong&gt; ShopEasy v3 (Modular Monolith → একটা সার্ভিস আলাদা, Event-driven)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;পর্যায় ৪: বিশেষজ্ঞ (চলমান)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] System design case study নিয়মিত অনুশীলন (URL shortener, chat, feed, ride-hailing)&lt;/li&gt;
&lt;li&gt;[ ] পারফরম্যান্স প্রোফাইলিং, load test&lt;/li&gt;
&lt;li&gt;[ ] বড় সিস্টেমের ইঞ্জিনিয়ারিং ব্লগ পড়ুন (Netflix, Uber, Stripe, Discord)&lt;/li&gt;
&lt;li&gt;[ ] ওপেন সোর্স কোড পড়ুন (ASP.NET Core, eShop reference)&lt;/li&gt;
&lt;li&gt;[ ] নিজে লিখুন ও শেখান (ব্লগ, টক): শেখানোই গভীরতম শেখা&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;বই (প্রস্তাবিত):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt;: Martin Kleppmann (ডেটা সিস্টেমের বাইবেল)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Clean Architecture&lt;/em&gt;: Robert C. Martin&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Domain-Driven Design&lt;/em&gt;: Eric Evans; &lt;em&gt;Implementing DDD&lt;/em&gt;: Vaughn Vernon&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Building Microservices&lt;/em&gt;: Sam Newman&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;System Design Interview&lt;/em&gt; (Vol 1 ও 2): Alex Xu&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Release It!&lt;/em&gt;: Michael Nygard (resilience)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Fundamentals of Software Architecture&lt;/em&gt;: Mark Richards ও Neal Ford&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reference প্রজেক্ট (পড়ে শিখুন):&lt;/strong&gt; Microsoft এর &lt;code&gt;eShop&lt;/code&gt; (.NET microservices/Aspire), &lt;code&gt;eShopOnWeb&lt;/code&gt; (Clean Architecture)। (সংগ্রহশালার বর্তমান অবস্থা GitHub এ দেখে নিন।)&lt;/p&gt;

&lt;h3&gt;
  
  
  ১২.১০ সম্পূর্ণ চেকলিস্ট: নতুন সিস্টেম লঞ্চের আগে
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;স্থাপত্য ও কোড&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] প্রয়োজন ও non-functional লক্ষ্য লিখিত (SLO সহ)&lt;/li&gt;
&lt;li&gt;[ ] উপযুক্ত architecture style (সরলতম যা চলে)&lt;/li&gt;
&lt;li&gt;[ ] কোড স্তরবদ্ধ, DI, SOLID; ADR আছে&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ডেটা&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] সঠিক index; N+1 নেই; pagination আছে&lt;/li&gt;
&lt;li&gt;[ ] Concurrency ও transaction সঠিক&lt;/li&gt;
&lt;li&gt;[ ] Backup ও restore পরীক্ষিত; migration নিরাপদ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] ভার্সনিং, validation, DTO, Problem Details&lt;/li&gt;
&lt;li&gt;[ ] Idempotency (POST), rate limiting&lt;/li&gt;
&lt;li&gt;[ ] OpenAPI ডকুমেন্ট&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resilience&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Timeout, retry (backoff+jitter), circuit breaker&lt;/li&gt;
&lt;li&gt;[ ] Idempotent consumer, DLQ, Outbox&lt;/li&gt;
&lt;li&gt;[ ] Graceful degradation, health check&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;নিরাপত্তা&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] AuthN/AuthZ, BOLA যাচাই, HTTPS&lt;/li&gt;
&lt;li&gt;[ ] Secret Key Vault এ; dependency স্ক্যান&lt;/li&gt;
&lt;li&gt;[ ] CORS, header, input validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Structured log + correlation ID&lt;/li&gt;
&lt;li&gt;[ ] Metric (golden signals + business) ও trace (OTel)&lt;/li&gt;
&lt;li&gt;[ ] Alert, runbook, dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] CI/CD, স্বয়ংক্রিয় test&lt;/li&gt;
&lt;li&gt;[ ] Container, IaC, নিরাপদ deploy ও rollback&lt;/li&gt;
&lt;li&gt;[ ] Load test; খরচ বাজেট alert&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  উপসংহার: "বস" হওয়ার আসল রহস্য
&lt;/h3&gt;

&lt;p&gt;আপনি এই গাইডে যা শিখলেন, তার সারকথা কয়েকটি বাক্যে:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;সরল দিয়ে শুরু করুন।&lt;/strong&gt; জটিলতা অর্জন করতে হয়, উপহার নয়।&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;মাপুন, অনুমান করবেন না।&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ব্যর্থতা অনিবার্য;&lt;/strong&gt; সেই অনুযায়ী ডিজাইন করুন।&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;প্রতিটি সিদ্ধান্ত একটা ট্রেড-অফ;&lt;/strong&gt; কী পেলেন আর কী ছাড়লেন, তা জানুন ও বলুন।&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;প্রয়োজনের আগে scale করবেন না, প্রয়োজনের পরে ফেলেও রাখবেন না।&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;কোড অন্যের জন্য লিখুন&lt;/strong&gt; (ছয় মাস পরের আপনিও "অন্য")।&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;প্রযুক্তি টিম ও ব্যবসার সেবা করে।&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;সিস্টেম ডিজাইন কোনো মুখস্থ বিদ্যা নয়; এটা &lt;strong&gt;চিন্তার পদ্ধতি।&lt;/strong&gt; একজন সত্যিকারের বস প্রতিটি নতুন সমস্যায় জিজ্ঞেস করেন: &lt;em&gt;"প্রয়োজন কী? সীমা কী? সরলতম সমাধান কী? কোথায় ভাঙবে? এর দাম কী?"&lt;/em&gt; এই পাঁচটি প্রশ্ন যে অভ্যাসে পরিণত করেছে, সে নতুন যেকোনো প্রযুক্তি এক সপ্তাহে বুঝে ফেলতে পারে।&lt;/p&gt;

&lt;p&gt;আপনার পরবর্তী পদক্ষেপ: একটা ছোট প্রজেক্ট নিন, ShopEasy হোক বা নিজের আইডিয়া। এই গাইডের ক্রমে একটা একটা করে প্রয়োগ করুন, ভুল করুন, মাপুন, শিখুন। পড়া আর করার মধ্যে যে ফাঁক, সেটাই আপনার আসল শিক্ষক।&lt;/p&gt;

&lt;p&gt;শুভকামনা।&lt;/p&gt;




&lt;p&gt;&lt;em&gt;এই লেখার সংখ্যা, লাইব্রেরির নাম/লাইসেন্স ও সেবার ফিচার সময়ের সাথে বদলায়; প্রয়োগের আগে সংশ্লিষ্ট অফিসিয়াল ডকুমেন্ট দেখে নিন।&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>LINQ Interview Questions for Senior Developers in 2026 — Deferred Execution, IQueryable vs IEnumerable, Performance &amp; Pitfalls</title>
      <dc:creator>kirandeepjassal-crypto</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:31:19 +0000</pubDate>
      <link>https://dev.to/kirandeepjassalcrypto/linq-interview-questions-for-senior-developers-in-2026-deferred-execution-iqueryable-vs-49bc</link>
      <guid>https://dev.to/kirandeepjassalcrypto/linq-interview-questions-for-senior-developers-in-2026-deferred-execution-iqueryable-vs-49bc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Junior LINQ interviews ask "what does &lt;code&gt;Where&lt;/code&gt; do." Senior interviews ask &lt;em&gt;why your query hit the database three times to render one number&lt;/em&gt;, &lt;em&gt;why &lt;code&gt;.Where(...)&lt;/code&gt; after &lt;code&gt;.ToList()&lt;/code&gt; quietly loads a billion rows into memory&lt;/em&gt;, and &lt;em&gt;why the same LINQ runs on SQL Server in one place and in C# in another&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LINQ looks like a tidy query language and is really two very different engines — in-memory delegates vs database expression trees — behind one syntax. Knowing which one you're in, and when it runs, is the whole senior game.&lt;/p&gt;

&lt;p&gt;This is 30 questions across &lt;strong&gt;the execution model, operators &amp;amp; correctness, performance &amp;amp; EF translation, and advanced pitfalls&lt;/strong&gt;. Each has a short answer, a deep dive with C# code, and the senior signal the interviewer is listening for. Every example is from &lt;strong&gt;Mattrx&lt;/strong&gt; — a real multi-tenant platform on &lt;strong&gt;.NET 9 / EF Core 9&lt;/strong&gt; over &lt;strong&gt;Azure SQL&lt;/strong&gt;, with &lt;code&gt;Campaigns&lt;/code&gt; (~4M rows) and &lt;code&gt;CampaignEvents&lt;/code&gt; (~1.2B rows).&lt;/p&gt;

&lt;h2&gt;
  
  
  Block A — the execution model
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. What is deferred execution?&lt;/strong&gt;&lt;br&gt;
A LINQ query defines &lt;em&gt;what&lt;/em&gt; to compute but doesn't run until you enumerate it — the work is deferred to the moment you iterate (or call a materializing operator). Because it's lazy, the query reflects the data &lt;em&gt;at enumeration time&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// defines the query — nothing runs yet&lt;/span&gt;
&lt;span class="n"&gt;campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newlyActiveCampaign&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// added BEFORE we enumerate...&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="c1"&gt;// query RUNS here — newCampaign is included&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. What operators trigger immediate execution?&lt;/strong&gt;&lt;br&gt;
Anything that must produce a concrete result: &lt;code&gt;ToList&lt;/code&gt;/&lt;code&gt;ToArray&lt;/code&gt;/&lt;code&gt;ToDictionary&lt;/code&gt;, &lt;code&gt;Count&lt;/code&gt;/&lt;code&gt;Sum&lt;/code&gt;/&lt;code&gt;Min&lt;/code&gt;/&lt;code&gt;Max&lt;/code&gt;, &lt;code&gt;First&lt;/code&gt;/&lt;code&gt;Single&lt;/code&gt;/&lt;code&gt;Last&lt;/code&gt;, &lt;code&gt;Any&lt;/code&gt;/&lt;code&gt;All&lt;/code&gt;, and a &lt;code&gt;foreach&lt;/code&gt;. Operators returning &lt;code&gt;IEnumerable&lt;/code&gt;/&lt;code&gt;IQueryable&lt;/code&gt; are deferred; operators returning a value or materialized collection execute now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Which operators stream and which buffer?&lt;/strong&gt;&lt;br&gt;
Streaming operators (&lt;code&gt;Where&lt;/code&gt;, &lt;code&gt;Select&lt;/code&gt;, &lt;code&gt;Take&lt;/code&gt;, &lt;code&gt;Skip&lt;/code&gt;) produce elements one at a time — constant memory. Buffering operators (&lt;code&gt;OrderBy&lt;/code&gt;, &lt;code&gt;GroupBy&lt;/code&gt;, &lt;code&gt;Reverse&lt;/code&gt;, &lt;code&gt;Distinct&lt;/code&gt;) must consume the entire source before yielding the first result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Where/Select:  pull 1 -&amp;gt; filter/map -&amp;gt; yield 1  (streaming, O(1) extra memory)
OrderBy:       pull ALL -&amp;gt; sort -&amp;gt; then yield    (buffering, O(n) memory)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. What is the multiple-enumeration bug?&lt;/strong&gt;&lt;br&gt;
Because queries are deferred, enumerating the same query variable twice re-runs it twice — two DB round trips for &lt;code&gt;IQueryable&lt;/code&gt;. Fix by materializing once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// DB round trip #1&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// DB round trip #2&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// DB round trip #3  — three queries for "one" query!&lt;/span&gt;

&lt;span class="c1"&gt;// FIX: materialize once, then work with the list in memory.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. IQueryable vs IEnumerable — what's the real difference?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; runs LINQ in memory using compiled delegates (LINQ to Objects). &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt; builds an expression tree a provider (EF Core) translates to SQL and runs on the database. Same &lt;code&gt;Where&lt;/code&gt;, two totally different engines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IQueryable&amp;lt;T&amp;gt;  --(expression tree)--&amp;gt; EF provider --&amp;gt; SQL --&amp;gt; runs on the DB
IEnumerable&amp;lt;T&amp;gt; --(compiled delegate)--&amp;gt; runs in memory on the app server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Query syntax vs method syntax — any real difference?&lt;/strong&gt;&lt;br&gt;
None in capability — query syntax is compiler sugar that lowers to the same method calls. Query syntax reads better for joins/&lt;code&gt;group by&lt;/code&gt;/&lt;code&gt;let&lt;/code&gt;; method syntax exposes operators query syntax lacks (&lt;code&gt;Count&lt;/code&gt;, &lt;code&gt;Take&lt;/code&gt;, &lt;code&gt;First&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What role do expression trees play?&lt;/strong&gt;&lt;br&gt;
For &lt;code&gt;IQueryable&lt;/code&gt;, a lambda isn't compiled to a delegate — it's captured as an expression tree, code as data. EF Core walks that tree and translates it to SQL. Untranslatable C# is exactly what breaks the translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. How is deferred execution actually implemented?&lt;/strong&gt;&lt;br&gt;
With iterators — &lt;code&gt;yield return&lt;/code&gt;. An operator like &lt;code&gt;Where&lt;/code&gt; returns a state machine that produces the next element only when asked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A custom streaming operator — deferred, one element at a time.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WhereActive&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// runs only as the caller pulls&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Block B — operators &amp;amp; correctness
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;9. Select vs SelectMany?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Select&lt;/code&gt; maps each element to one result (1-&amp;gt;1); if that result is a collection you get a nested sequence. &lt;code&gt;SelectMany&lt;/code&gt; flattens — mapping each element to a collection and concatenating (1-&amp;gt;many).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// IEnumerable&amp;lt;IEnumerable&amp;lt;Event&amp;gt;&amp;gt;  (nested)&lt;/span&gt;
&lt;span class="n"&gt;campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// IEnumerable&amp;lt;Event&amp;gt;               (flattened)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;10. First vs FirstOrDefault vs Single?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;First&lt;/code&gt; returns the first match, throws if none. &lt;code&gt;FirstOrDefault&lt;/code&gt; returns &lt;code&gt;default&lt;/code&gt; if none. &lt;code&gt;Single&lt;/code&gt; expects exactly one and throws on zero &lt;em&gt;or more than one&lt;/em&gt;. &lt;code&gt;Single&lt;/code&gt; costs more because it verifies uniqueness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. How does GroupBy work, and where does it run?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;GroupBy&lt;/code&gt; partitions by a key. In LINQ-to-Objects it buffers the whole source; in EF Core it translates to SQL &lt;code&gt;GROUP BY&lt;/code&gt; — as long as you project aggregates, not the raw groups.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;perDay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DailyCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;12. Join vs GroupJoin vs navigation properties?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Join&lt;/code&gt; is an inner join. &lt;code&gt;GroupJoin&lt;/code&gt; gives each left element its group of right matches (basis for left joins via &lt;code&gt;SelectMany&lt;/code&gt; + &lt;code&gt;DefaultIfEmpty&lt;/code&gt;). In EF Core, navigation properties express the relationship and EF generates the JOIN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. When do you use Aggregate?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Aggregate&lt;/code&gt; is the general fold — threading an accumulator through the sequence. Use it for custom reductions; for sums/counts/averages use the specialized operators (clearer, translatable to SQL). &lt;code&gt;Aggregate&lt;/code&gt; usually forces client evaluation on &lt;code&gt;IQueryable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14. How do you dedup — Distinct vs DistinctBy vs GroupBy?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Distinct&lt;/code&gt; removes duplicate elements by equality. &lt;code&gt;DistinctBy&lt;/code&gt; (.NET 6+) dedups by a key selector. &lt;code&gt;GroupBy(key).Select(g =&amp;gt; g.First())&lt;/code&gt; is the older idiom. &lt;code&gt;Distinct&lt;/code&gt; on objects depends on &lt;code&gt;Equals&lt;/code&gt;/&lt;code&gt;GetHashCode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15. OrderBy / ThenBy — what should you watch for?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;OrderBy&lt;/code&gt; then &lt;code&gt;ThenBy&lt;/code&gt; for multi-key sorts (a second &lt;code&gt;OrderBy&lt;/code&gt; replaces the first). Ordering buffers the whole source, and you must order &lt;em&gt;before&lt;/em&gt; &lt;code&gt;Skip&lt;/code&gt;/&lt;code&gt;Take&lt;/code&gt; for deterministic pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16. Skip/Take for pagination — the gotchas?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Skip(n).Take(m)&lt;/code&gt; is offset pagination; it must follow an &lt;code&gt;OrderBy&lt;/code&gt;, and large offsets get slow (the DB scans skipped rows). For deep pages, prefer keyset/cursor pagination (&lt;code&gt;Where(id &amp;gt; lastSeen).Take(m)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;17. How does equality work for Union/Intersect/Except/Distinct?&lt;/strong&gt;&lt;br&gt;
The set operators compare with &lt;code&gt;Equals&lt;/code&gt;/&lt;code&gt;GetHashCode&lt;/code&gt; (or an &lt;code&gt;IEqualityComparer&amp;lt;T&amp;gt;&lt;/code&gt;). Classes that don't override equality compare by reference — use &lt;code&gt;record&lt;/code&gt;s (value equality) or a custom comparer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Block C — performance &amp;amp; EF translation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;18. What LINQ translates to SQL, and what throws?&lt;/strong&gt;&lt;br&gt;
Standard operators over mapped columns translate; arbitrary C# methods EF can't represent can't be translated. EF Core 3.0+ throws rather than silently evaluating on the client — so an untranslatable query fails loudly instead of loading everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;19. Where does the N+1 problem hide in LINQ?&lt;/strong&gt;&lt;br&gt;
In a &lt;code&gt;Select&lt;/code&gt; or loop that triggers a per-element query. One list query plus N element queries. Fix by projecting the related data in the same query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// N+1: the loop issues one COUNT query per campaign.&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Campaigns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CountAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CampaignId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// FIX: one query that projects the aggregate.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Campaigns&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CampaignSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;20. How does projection improve performance?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Select&lt;/code&gt;-ing into a DTO makes EF emit a narrower &lt;code&gt;SELECT&lt;/code&gt; — only the columns you named, no full entity, no change tracking. For reads, projecting is almost always faster than materializing whole entities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;21. Count() vs Any() — which for existence checks?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Any()&lt;/code&gt;. It translates to SQL &lt;code&gt;EXISTS&lt;/code&gt; and short-circuits at the first row; &lt;code&gt;Count() &amp;gt; 0&lt;/code&gt; counts every matching row first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CampaignId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;         &lt;span class="c1"&gt;// counts ALL matches&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AnyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CampaignId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// EXISTS — stops at first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;22. Does operator order matter?&lt;/strong&gt;&lt;br&gt;
Yes. Filter (&lt;code&gt;Where&lt;/code&gt;) before projecting and ordering so the pipeline works on fewer rows; order before paging. On &lt;code&gt;IQueryable&lt;/code&gt; EF composes the whole tree into one SQL statement, but on &lt;code&gt;IEnumerable&lt;/code&gt; order controls how many elements each stage processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;23. What's the danger of calling ToList() too early?&lt;/strong&gt;&lt;br&gt;
It materializes the query at that point — so any &lt;code&gt;Where&lt;/code&gt;/&lt;code&gt;Select&lt;/code&gt; &lt;em&gt;after&lt;/em&gt; it runs in memory on all the rows you just pulled, not in SQL. Keep the query as &lt;code&gt;IQueryable&lt;/code&gt; until the last moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;24. What is async LINQ, and its limits?&lt;/strong&gt;&lt;br&gt;
EF Core provides async materializers — &lt;code&gt;ToListAsync&lt;/code&gt;, &lt;code&gt;FirstOrDefaultAsync&lt;/code&gt;, &lt;code&gt;AnyAsync&lt;/code&gt;, &lt;code&gt;CountAsync&lt;/code&gt;. But there's no &lt;code&gt;WhereAsync&lt;/code&gt;/&lt;code&gt;SelectAsync&lt;/code&gt;: only the &lt;em&gt;execution&lt;/em&gt; is async. For in-memory async streams, use &lt;code&gt;IAsyncEnumerable&lt;/code&gt;/&lt;code&gt;await foreach&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;25. How do compiled queries help?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;EF.CompileAsyncQuery&lt;/code&gt; caches the LINQ-&amp;gt;SQL translation so a very hot query skips re-building the expression tree each call. EF already caches query plans — reserve this for measured hot paths.&lt;/p&gt;
&lt;h2&gt;
  
  
  Block D — advanced, pitfalls &amp;amp; functional
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;26. What's the closure gotcha with LINQ in a loop?&lt;/strong&gt;&lt;br&gt;
A lambda captures the variable, not its value. &lt;code&gt;foreach&lt;/code&gt; gives a fresh variable per iteration (safe since C# 5), but a &lt;code&gt;for (var i...)&lt;/code&gt; capturing &lt;code&gt;i&lt;/code&gt; still shares one binding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predicates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="n"&gt;predicates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   &lt;span class="c1"&gt;// BUG: all capture the same i (= Count)&lt;/span&gt;
&lt;span class="c1"&gt;// FIX: copy into a local, or use foreach (fresh binding per iteration).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;27. What do let, into, and multiple from do?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;let&lt;/code&gt; introduces a computed value inside a query; &lt;code&gt;into&lt;/code&gt; continues a query after a &lt;code&gt;group&lt;/code&gt;/&lt;code&gt;select&lt;/code&gt; (query continuation); multiple &lt;code&gt;from&lt;/code&gt; clauses are &lt;code&gt;SelectMany&lt;/code&gt;. Query-syntax conveniences that lower to method calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;28. How do you write a custom LINQ operator?&lt;/strong&gt;&lt;br&gt;
An extension method on &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; — usually with &lt;code&gt;yield return&lt;/code&gt; for deferred behavior. For &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt;, a custom operator must build/compose an expression tree to stay translatable, which is much harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;29. What LINQ pitfalls do you check for in production?&lt;/strong&gt;&lt;br&gt;
Multiple enumeration, &lt;code&gt;ToList()&lt;/code&gt; too early, &lt;code&gt;Count() &amp;gt; 0&lt;/code&gt; instead of &lt;code&gt;Any()&lt;/code&gt;, N+1 from per-element queries, missing &lt;code&gt;OrderBy&lt;/code&gt; before paging, un-materialized queries escaping a disposed context, and set operations on classes without value equality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;30. When should you NOT use LINQ?&lt;/strong&gt;&lt;br&gt;
In hot, tight loops where the allocations (iterators, closures, delegates) measurably hurt — a plain &lt;code&gt;for&lt;/code&gt; loop can be markedly faster and allocation-free. Favor LINQ for clarity; drop to loops where a profiler says it matters.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The through-line:&lt;/strong&gt; LINQ is one syntax over two engines — in-memory delegates and database expression trees. Deferral means the query runs on enumeration (so materialize once), the &lt;code&gt;IQueryable&lt;/code&gt;/&lt;code&gt;IEnumerable&lt;/code&gt; boundary decides SQL-vs-memory, projection narrows the SQL, &lt;code&gt;Any&lt;/code&gt; beats &lt;code&gt;Count &amp;gt; 0&lt;/code&gt;, and a mid-query &lt;code&gt;ToList()&lt;/code&gt; silently drags work back to the app server. Answer each question with &lt;em&gt;when it runs, where it runs, and what SQL it becomes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://prepstack.co.in/blog/linq-senior-interview-questions-deferred-execution-iqueryable-performance-deep-answers" rel="noopener noreferrer"&gt;PrepStack&lt;/a&gt;. The full original has all the code and senior-signal notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>interview</category>
      <category>database</category>
    </item>
    <item>
      <title>EasySave ProSoft : orchestrer des sauvegardes fiables avec .NET 8</title>
      <dc:creator>Tahar Chtioui</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:21:45 +0000</pubDate>
      <link>https://dev.to/javascriptx/easysave-prosoft-orchestrer-des-sauvegardes-fiables-avec-net-8-446k</link>
      <guid>https://dev.to/javascriptx/easysave-prosoft-orchestrer-des-sauvegardes-fiables-avec-net-8-446k</guid>
      <description>&lt;h1&gt;
  
  
  Objectif
&lt;/h1&gt;

&lt;p&gt;EasySave ProSoft automatise des sauvegardes complètes et différentielles tout en donnant une visibilité en temps réel sur leur progression. Le projet associe une interface WPF bilingue, une architecture MVVM et un moteur de jobs capable de gérer plusieurs opérations en parallèle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fonctionnalités
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Nombre illimité de jobs configurables&lt;/li&gt;
&lt;li&gt;Sauvegarde complète et différentielle&lt;/li&gt;
&lt;li&gt;Actions Play, Pause et Stop&lt;/li&gt;
&lt;li&gt;Priorité de certains fichiers&lt;/li&gt;
&lt;li&gt;Limitation des gros transferts simultanés&lt;/li&gt;
&lt;li&gt;Chiffrement avec CryptoSoft&lt;/li&gt;
&lt;li&gt;Journaux JSON ou XML&lt;/li&gt;
&lt;li&gt;Utilisation en interface graphique ou en ligne de commande&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Les stratégies de sauvegarde isolent les différences entre les modes complets et différentiels. L’orchestration parallèle tient compte des priorités, de la concurrence et des commandes utilisateur. La journalisation peut être centralisée dans un service conteneurisé afin d’améliorer la traçabilité.&lt;/p&gt;

&lt;h2&gt;
  
  
  Points d’attention
&lt;/h2&gt;

&lt;p&gt;Une application de sauvegarde doit traiter les interruptions, les fichiers indisponibles, les conflits de destination et les états partiels. Le modèle de progression doit rester cohérent même lorsque plusieurs jobs sont actifs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JavaScript-X/EasySave_ProSoft" rel="noopener noreferrer"&gt;Voir le projet sur GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>docker</category>
      <category>programming</category>
    </item>
    <item>
      <title>440 MB of Closures on Pure Cache Hits</title>
      <dc:creator>Sukhpinder Singh</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:18:35 +0000</pubDate>
      <link>https://dev.to/ssukhpinder/440-mb-of-closures-on-pure-cache-hits-35l</link>
      <guid>https://dev.to/ssukhpinder/440-mb-of-closures-on-pure-cache-hits-35l</guid>
      <description>&lt;p&gt;I had a lookup cache on a hot path, a &lt;code&gt;ConcurrentDictionary&amp;lt;int, string&amp;gt;&lt;/code&gt; that maps a tenant id to a formatted key. After warmup it's basically all hits. The entry's already there, you read it, you move on. So when I saw the gen-0 collections ticking over under steady load, I went looking for the allocation somewhere else first. The cache couldn't be it. It's hits. Hits don't build anything.&lt;/p&gt;

&lt;p&gt;Except mine did. The factory I handed to &lt;code&gt;GetOrAdd&lt;/code&gt; was quietly allocating an object on every single call, including the millions of calls where it never ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two overloads that read the same and aren't
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GetOrAdd&lt;/code&gt; has more than one factory shape. The one everybody reaches for is &lt;code&gt;GetOrAdd(key, k =&amp;gt; Build(k))&lt;/code&gt;, taking a &lt;code&gt;Func&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;. There's another that takes &lt;code&gt;Func&amp;lt;TKey, TArg, TValue&amp;gt;&lt;/code&gt; plus a state argument: &lt;code&gt;GetOrAdd(key, (k, arg) =&amp;gt; Build(k, arg), arg)&lt;/code&gt;. On the surface they do the same thing, and most comparisons of them talk about the factory invocation.&lt;/p&gt;

&lt;p&gt;But the invocation isn't where my cost was. Mine was at the call site, before &lt;code&gt;GetOrAdd&lt;/code&gt; decided anything. My factory closed over a value that changed every time through the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                       &lt;span class="c1"&gt;// different each iteration&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"tenant-42:user:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;salt&lt;/code&gt; is a fresh local each iteration, the compiler can't cache that lambda. It builds a new display-class instance to hold &lt;code&gt;salt&lt;/code&gt;, and a new delegate pointing at it, every time the line runs. The dictionary then looks up &lt;code&gt;key&lt;/code&gt;, finds it, hands back the existing string, and never touches my shiny new closure. I allocated it for nothing.&lt;/p&gt;

&lt;p&gt;The fix is the state-arg overload, with a &lt;code&gt;static&lt;/code&gt; lambda so there's physically nothing to capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"tenant-42:user:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value I need rides in as the argument. The lambda captures nothing, so the compiler caches a single delegate for the whole program and the call allocates zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  I counted it
&lt;/h2&gt;

&lt;p&gt;I don't trust my gut on allocation, so I wrote the smallest thing that shows it. A dictionary pre-filled with a thousand keys, then five million &lt;code&gt;GetOrAdd&lt;/code&gt; calls cycling over those keys, so after the warmup pass essentially everything is a hit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ConcurrentDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenant-42:user:"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both hammer loops are the ones above, wrapped in a method. The harness is nothing fancy, a stopwatch, a warmup call per path so the JIT is done, best of seven, allocations read around each pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;bestMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;Measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;bestMs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAllocatedBytesForCurrentThread&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAllocatedBytesForCurrentThread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bestMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;bestMs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bestMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Single-file console app, .NET 10, nothing else in it. Here's what came back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GetOrAdd calls          : 5,000,000  (hits on 1,000 keys)
capturing lambda time   :    112 ms
state-arg    time       :     24 ms
capturing lambda alloc  :    440,000,000 bytes
state-arg    alloc      :              0 bytes
bytes per capturing call: 88.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;88 bytes a call, on pure cache hits. The display class plus the delegate, every time, swept up a moment later having done no work. Across five million calls that's 440 MB of churn for a loop whose entire job was to read strings that already existed. The state-arg version did the identical reads at zero bytes, and came out roughly four and a half times faster because it wasn't feeding the collector.&lt;/p&gt;

&lt;p&gt;This is a container, not a lab bench, and I trust the ratio more than the millisecond count. But 440 MB against zero holds up anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this is a non-issue
&lt;/h2&gt;

&lt;p&gt;Here's the part that bit me and might save you a wild goose chase. This only costs you when the factory captures something that varies per call. If your lambda captures nothing, or captures a value that's fixed for the whole method, the compiler already caches the closure and allocates it once. I proved that to myself first with a factory that closed over a method-level constant and got a grand total of 96 bytes for the entire run. So don't go rewriting every &lt;code&gt;GetOrAdd&lt;/code&gt; you own. Go find the ones whose factory closes over a loop variable, a method argument that changes, a per-request value.&lt;/p&gt;

&lt;p&gt;And yes, the &lt;code&gt;static (k, s) =&amp;gt; ...&lt;/code&gt; form reads a little worse than the plain lambda. My honest opinion: on a hot path that's a trade I take without thinking, and everywhere else I leave the readable version alone.&lt;/p&gt;

&lt;p&gt;Since the usual repo push got blocked on this run, the whole sample is up above. Two hammer methods, the dictionary setup, the harness, drop them in a &lt;code&gt;dotnet new console&lt;/code&gt; on .NET 10 and it runs.&lt;/p&gt;

&lt;p&gt;What's the sneakiest allocation you've found hiding behind a line you were sure was free? I collect these.&lt;/p&gt;

&lt;p&gt;Right, back to it. Still putting counters on code that was never on trial.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Supabase .NET DevLog #16</title>
      <dc:creator>Guillaume Faas</dc:creator>
      <pubDate>Wed, 28 Oct 2026 07:27:51 +0000</pubDate>
      <link>https://dev.to/tr00d/supabase-net-devlog-16-49g3</link>
      <guid>https://dev.to/tr00d/supabase-net-devlog-16-49g3</guid>
      <description>&lt;p&gt;Welcome to our weekly log for the Supabase .NET SDK 🎉&lt;/p&gt;

&lt;p&gt;As mentioned in the previous post, I focused on the automatic generation of our feature compliance file, which is now done. &lt;/p&gt;

&lt;p&gt;The SDK provides annotations we place on public members to indicate which feature they relate to. After each push on master, the &lt;a href="https://github.com/supabase/supabase-csharp/blob/master/sdk-compliance.yaml" rel="noopener noreferrer"&gt;compliance file&lt;/a&gt; is regenerated and ready to be pulled by other systems. An additional step in our quality gate also verifies that every annotated feature exists in our capability matrix.&lt;/p&gt;

&lt;p&gt;The file is now my "north star", a backlog of its own: every missing feature is an item that I need to implement. FYI the SDK currently sits at roughly 50% parity with our platform.&lt;/p&gt;

&lt;p&gt;Since I joined Supabase four months ago, I focused on making the SDK robust enough to move fast(er). Now, we're building momentum and starting a new phase: catching up on the feature drift.&lt;/p&gt;

&lt;p&gt;As usual, stay tuned and happy coding!&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Split Two Million Rows and the GC Noticed</title>
      <dc:creator>Sukhpinder Singh</dc:creator>
      <pubDate>Wed, 28 Oct 2026 07:18:43 +0000</pubDate>
      <link>https://dev.to/ssukhpinder/i-split-two-million-rows-and-the-gc-noticed-2lgf</link>
      <guid>https://dev.to/ssukhpinder/i-split-two-million-rows-and-the-gc-noticed-2lgf</guid>
      <description>&lt;p&gt;I had a nightly job that reads a partner CSV and sums the amount column, just to sanity-check a total before the real import runs. Boring code. &lt;code&gt;row.Split(',')&lt;/code&gt;, grab field one, parse it, add it up. It worked the day I wrote it and I forgot about it.&lt;/p&gt;

&lt;p&gt;Then the job's memory graph grew a sawtooth I didn't like, and I opened a profiler expecting to find the database client or the file reader. Nope. The garbage collector was working full time on nothing but comma splitting. For a loop that reads &lt;em&gt;one&lt;/em&gt; column out of four.&lt;/p&gt;

&lt;h2&gt;
  
  
  string.Split is generous with memory
&lt;/h2&gt;

&lt;p&gt;Here's what I'd stopped seeing. &lt;code&gt;string.Split(',')&lt;/code&gt; doesn't just find the commas. It allocates a &lt;code&gt;string[]&lt;/code&gt; to hold the pieces, and then a brand-new &lt;code&gt;string&lt;/code&gt; for every field it carved out. Four columns means five allocations per row before I've parsed a single number. And I was throwing away three of those strings immediately, because I only wanted field one.&lt;/p&gt;

&lt;p&gt;Multiply that by a couple million rows and the GC has plenty to do.&lt;/p&gt;

&lt;p&gt;The newer &lt;code&gt;Span&amp;lt;char&amp;gt;.Split&lt;/code&gt; overloads take a different deal. You hand them a &lt;code&gt;Span&amp;lt;Range&amp;gt;&lt;/code&gt; to fill, they write the &lt;em&gt;boundaries&lt;/em&gt; of each field into it, and nothing gets copied. You slice the original string yourself when you actually need a piece. &lt;code&gt;double.Parse&lt;/code&gt; happily takes a &lt;code&gt;ReadOnlySpan&amp;lt;char&amp;gt;&lt;/code&gt;, so I never have to materialize the substring at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;SumWithSpanSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;stackalloc&lt;/span&gt; &lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// 4 columns, room to spare&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;stackalloc Range[8]&lt;/code&gt; lives on the stack and gets reused on every iteration, so the per-row cost is zero. Compare that to the version I actually shipped first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;SumWithStringSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  I counted it
&lt;/h2&gt;

&lt;p&gt;I don't trust my gut on allocations, so I built two million CSV-ish rows in memory (&lt;code&gt;id,amount,region,active&lt;/code&gt;), then summed the amount column both ways. A stopwatch, a warmup call per path, best time out of seven, and the allocation counter read around each pass. This is a container, not a lab bench, so I only care about the ratio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rows: 2,000,000   Runtime: .NET 10.0.14

string.Split       231.8 ms      383,039,160 bytes   sum=9960642900.00
span Split         122.5 ms               40 bytes   sum=9960642900.00

time:  span is 1.9x faster
alloc: span allocated 40 bytes
sums match: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;365 megabytes versus 40 bytes. The 40 bytes is the harness breathing, not the loop. And the loop got about 1.8x faster across repeated runs, which is the part I didn't expect, I went in thinking I'd just save memory. Turns out all that allocating and collecting isn't free on the clock either.&lt;/p&gt;

&lt;p&gt;The sums matching is the line I care about most. A faster parser that quietly reads the wrong column is worse than a slow correct one.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I'd leave string.Split alone
&lt;/h2&gt;

&lt;p&gt;Most of the time, honestly. If I'm reading a 30-line config file once at startup, &lt;code&gt;string.Split&lt;/code&gt; is clearer and the allocations vanish into the noise. I'm not going to stack-allocate a range buffer to save microseconds on something that runs once. The span version is also fussier: you cap the number of fields at the size of your &lt;code&gt;Span&amp;lt;Range&amp;gt;&lt;/code&gt;, and if a row has more commas than you planned for, the extra columns fold into the last range instead of throwing. For my fixed-shape partner feed that's fine. For arbitrary user input I'd check the returned count before trusting &lt;code&gt;fields[1]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My rule of thumb: reach for span Split when the same parse runs in a hot loop over a lot of rows, and leave the friendly version everywhere else. The readability cost is real, so I want the allocation win to be real too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the harness, if you want to run it
&lt;/h2&gt;

&lt;p&gt;I usually link these out to a repo, but the push to my samples repo was blocked on this run, so here's the whole thing in one place. Drop it in a &lt;code&gt;net10.0&lt;/code&gt; console project (&lt;code&gt;&amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;&lt;/code&gt;) and &lt;code&gt;dotnet run -c Release&lt;/code&gt;. The two &lt;code&gt;Sum...&lt;/code&gt; methods above go right below this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Globalization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RowCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Build the rows once: id,amount,region,active  e.g. "1048576,37.52,emea,1"&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RowCount&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;RowCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;9973&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"emea"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"apac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"amer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"latam"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;Measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// warm up the JIT and the code paths&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;bytesBest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTotalAllocatedBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;precise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartNew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTotalAllocatedBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;precise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;bytesBest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytesBest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Measure&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SumWithStringSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Measure&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SumWithSpanSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"string.Split  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ms  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes  sum=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"span Split    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ms  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes  sum=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've got a parsing loop that shows up on your allocation graph, this is a cheap swap to try. What's the worst allocation hot spot you've found hiding behind a one-line convenience method?&lt;/p&gt;

&lt;p&gt;Catch you in the next one — still putting stopwatches on code nobody asked me to time.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Should approval thresholds live in your .NET workflow or in a rule engine?</title>
      <dc:creator>Optimajet Limited</dc:creator>
      <pubDate>Wed, 28 Oct 2026 06:28:37 +0000</pubDate>
      <link>https://dev.to/optimajet/should-approval-thresholds-live-in-your-net-workflow-or-in-a-rule-engine-39fi</link>
      <guid>https://dev.to/optimajet/should-approval-thresholds-live-in-your-net-workflow-or-in-a-rule-engine-39fi</guid>
      <description>&lt;p&gt;Keep approval thresholds in your .NET workflow while developers own them, the thresholds change about once a year, there are two or three of them, and one service uses them. Move them to a rule engine when a business owner wants to edit them without a developer. Do the same when the thresholds change quarterly or more often, when the list grows into a table, or when several services need the same decision. After the move, a workflow action calls the rule engine and stores the decision with its rule version.&lt;/p&gt;

&lt;p&gt;Mike Lukinov, co-founder of Optimajet, sets out that rule in &lt;a href="https://workflowengine.io/blog/rule-engine-vs-workflow-engine/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=rule-engine-vs-workflow-engine" rel="noopener noreferrer"&gt;Rule Engine vs Workflow Engine&lt;/a&gt;. Mike writes from the workflow side. Workflow Engine by Optimajet is a .NET workflow engine with no rule engine of its own. The post compares Workflow Engine with the rule engines it would call: NRules, Microsoft RulesEngine and DMN (Decision Model and Notation) engines. Workflow Engine Free runs the workflow side of this pattern. It needs no license key. Its license covers personal use and internal commercial use, with attribution.&lt;/p&gt;

&lt;p&gt;The product facts were checked on Workflow Engine 23.0.0 on 27 October 2026. The facts about NRules, Microsoft RulesEngine and DMN were checked on their own pages the same day. The thresholds in the examples are illustrative.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a rule engine does and what a workflow engine does
&lt;/h2&gt;

&lt;p&gt;A rule engine evaluates facts against explicit rules and returns a decision, usually without keeping state between calls. A workflow engine stores every running process and moves it from step to step. Take a purchase request for 60,000 from a preferred supplier. The rule engine answers that the request needs CFO approval. Workflow Engine then holds the request in its database, perhaps for days, until the CFO acts. The same supplier's request for 12,000 gets Auto-approve and finishes in the same call. When a request only needs an answer, such as a live checkout price, the rule engine alone is enough.&lt;/p&gt;

&lt;p&gt;One name can mislead .NET readers. In Workflow Engine, a Rule is a permission check that decides which user may execute a command. It is not a business rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three places a threshold can live
&lt;/h2&gt;

&lt;p&gt;In Workflow Engine, a scheme describes a process. It defines the activities (steps), the transitions between them and the commands that people execute. A threshold has two homes inside the workflow and one outside it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An Expression condition in the scheme.&lt;/strong&gt; By default, it is a C# expression that reads a process parameter. It sits on a transition. A change ships as a new scheme version. Running instances keep the version they started with until you migrate them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An Action condition in your code.&lt;/strong&gt; Your &lt;code&gt;IWorkflowActionProvider&lt;/code&gt; implements a named condition that can read the threshold from configuration or a table. A change needs no new scheme version, but a developer still owns the threshold. Nothing records which value routed each request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A rule engine.&lt;/strong&gt; It sits outside the workflow. It takes the thresholds that business owners edit, that change quarterly or more often, that grow into tables, or that several services share.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Which rule engine for which rules
&lt;/h2&gt;

&lt;p&gt;Workflow Engine's own Decision Table activity is not a rule engine. Each row holds a transition condition and a target activity. The rows are stored in the scheme. The activity has no hit policies and no repository for business users. A hit policy is the rule that picks the result when several rows match.&lt;/p&gt;

&lt;p&gt;Decision tables that analysts edit belong in a DMN engine or a business rules management system (BRMS). Both store rules as data and version them. DMN, the decision standard of the Object Management Group, reached &lt;a href="https://www.omg.org/spec/DMN/1.6" rel="noopener noreferrer"&gt;version 1.6&lt;/a&gt; in September 2026. For rules that developers own, two MIT-licensed .NET libraries are common choices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nrules.net/" rel="noopener noreferrer"&gt;NRules&lt;/a&gt;&lt;/strong&gt; (1.0.4, published 3 February 2026) runs rules that you write in C# with an internal DSL. So a rule change is normally a code change. The project also offers JSON serialization and a text rule language called Rule#. NRules can fire several matching rules and infer new facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/RulesEngine" rel="noopener noreferrer"&gt;Microsoft RulesEngine&lt;/a&gt;&lt;/strong&gt; (6.0.1, published 3 July 2026) keeps rules as JSON outside the code. It suits rules that change as data but need no full rule repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Boundary tests catch row-order and threshold mistakes
&lt;/h2&gt;

&lt;p&gt;Once a threshold moves from Workflow Engine to a rule engine's table, the order of the rows is part of the logic. Under the DMN First hit policy, the engine checks the rows in order and returns the first match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchase approval routing, First hit policy (from the full post)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule order&lt;/th&gt;
&lt;th&gt;Supplier status&lt;/th&gt;
&lt;th&gt;Amount (USD)&lt;/th&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;Reject&lt;/td&gt;
&lt;td&gt;Supplier is on the block list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;≥ 50,000&lt;/td&gt;
&lt;td&gt;CFO approval&lt;/td&gt;
&lt;td&gt;Above executive threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;New&lt;/td&gt;
&lt;td&gt;≥ 5,000&lt;/td&gt;
&lt;td&gt;Procurement review&lt;/td&gt;
&lt;td&gt;New supplier above review threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Preferred&lt;/td&gt;
&lt;td&gt;&amp;lt; 50,000&lt;/td&gt;
&lt;td&gt;Auto-approve&lt;/td&gt;
&lt;td&gt;Preferred supplier under the limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;Manager approval&lt;/td&gt;
&lt;td&gt;Default route&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you move row 1 below row 2, a blocked supplier's request for 60,000 goes to the CFO instead of being rejected. If you raise row 2 to 75,000 and leave row 4 at 50,000, two other routes change by accident. A preferred supplier's request for 60,000 goes to the manager. A new supplier's request for 60,000 goes to procurement review instead of the CFO. Boundary tests at 49,999.99, 50,000, 74,999.99 and 75,000 catch both mistakes before release. When rows should never overlap, the DMN Unique hit policy turns an overlap into an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a workflow action calls the rule engine
&lt;/h2&gt;

&lt;p&gt;With Workflow Engine, the two engines connect through a contract. It says which facts go in, which decision and rule version come out, and what happens when the call is late or fails. A workflow action is the code that a step of the scheme runs. In this pattern, the action does four things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It reads the facts from the request's process parameters, such as the supplier status and the amount.&lt;/li&gt;
&lt;li&gt;It calls the rule service and gives the call its own deadline, for example 500 ms.&lt;/li&gt;
&lt;li&gt;It stores the route and the rule version as persistent parameters, which are saved with the request.&lt;/li&gt;
&lt;li&gt;When the call is late or fails, the action stores the decision NeedsReview and the reason instead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Transitions with Expression conditions then route the request by the stored decision. Workflow Engine checks an Otherwise transition last. This transition sends all other requests to a step where a person reviews them. Requests with the decision NeedsReview go there too. When the call to the rule service is late or fails, even a request that the rules would approve waits for a person. The reason is stored as well.&lt;/p&gt;

&lt;p&gt;Three things need care.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The deadline needs a call that can be canceled.&lt;/strong&gt; The action passes a cancellation token to the call. An HTTP call to a rule service stops when the token fires. A rule engine inside the same application stops only if its API accepts the token. NRules checks the token between rule firings. Give any engine that ignores the token its own time limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A call that changes something can run twice.&lt;/strong&gt; After a restart, the step can run again. Such a call needs an idempotency key or an outbox. An idempotency key is a value that makes a repeated call harmless. An outbox holds messages that are saved in the same database transaction and sent after the transaction commits. A call that only computes can simply run again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A second engine is a second system.&lt;/strong&gt; It adds a network call, its own ways to fail and its own release cycle for rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stored rule version records which rules made the decision. It does not pin the request to those rules. Suppose a request is submitted before a threshold changes and is evaluated after the change. That request takes the new route. A threshold in an Expression condition is pinned for free, because each request keeps its scheme version. With a rule engine, the contract has to carry a version or a creation time. The rule service also has to keep old versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it takes to run the contract on .NET
&lt;/h2&gt;

&lt;p&gt;Workflow Engine Free includes actions, persistent parameters and Expression conditions, which is all the contract needs. It runs with up to 10 workflow definitions (schemes) and 4 execution threads. A public web application or a SaaS product needs the Subscription, SaaS or Enterprise Edition of Workflow Engine NEO. Workflow Engine NEO is the HTTP API product of the Workflow Engine Family.&lt;/p&gt;

&lt;p&gt;So keep each threshold where its owner can change it. When a threshold moves to a rule engine, keep the route in the workflow and store the rule version with every decision. The full post adds a side-by-side table of the two engines. It also answers whether rules engines are outdated now that AI models score requests. Both are in &lt;a href="https://workflowengine.io/blog/rule-engine-vs-workflow-engine/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=rule-engine-vs-workflow-engine" rel="noopener noreferrer"&gt;Rule Engine vs Workflow Engine, and Where a Business Rules Engine Fits&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>workflow</category>
      <category>dmn</category>
    </item>
    <item>
      <title>Stop Trimming \r Off Every Regex Match. .NET 11 Has AnyNewLine</title>
      <dc:creator>JKC</dc:creator>
      <pubDate>Wed, 28 Oct 2026 05:55:56 +0000</pubDate>
      <link>https://dev.to/iamprincejkc/stop-trimming-r-off-every-regex-match-net-11-has-anynewline-3h4d</link>
      <guid>https://dev.to/iamprincejkc/stop-trimming-r-off-every-regex-match-net-11-has-anynewline-3h4d</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In .NET 11, &lt;code&gt;RegexOptions.AnyNewLine&lt;/code&gt; makes &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;\Z&lt;/code&gt;, and &lt;code&gt;.&lt;/code&gt; treat &lt;code&gt;\r\n&lt;/code&gt;, &lt;code&gt;\r&lt;/code&gt;, and the Unicode line breaks as newlines, not just &lt;code&gt;\n&lt;/code&gt;. No more &lt;code&gt;\r?$&lt;/code&gt;, and no more values that end in an invisible carriage return. .NET 11 is at Release Candidate 2 right now: go-live licensed, but not final yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The character nobody can see
&lt;/h2&gt;

&lt;p&gt;You write a regex that grabs a line. It passes every test, because the test strings were typed in C# with &lt;code&gt;\n&lt;/code&gt;. Then a real file arrives from Windows, and every value you capture ends with &lt;code&gt;\r&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo\r\nbar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;@".+$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three letters, length 4. The fourth one is &lt;code&gt;\r&lt;/code&gt;, and it only shows up later, when &lt;code&gt;"foo" == value&lt;/code&gt; is mysteriously false.&lt;/p&gt;

&lt;p&gt;The reason is old and simple. .NET's regex engine has always treated only &lt;code&gt;\n&lt;/code&gt; as a newline. In &lt;code&gt;Multiline&lt;/code&gt; mode, &lt;code&gt;$&lt;/code&gt; matches before &lt;code&gt;\n&lt;/code&gt;, and &lt;code&gt;.&lt;/code&gt; happily matches &lt;code&gt;\r&lt;/code&gt; because, as far as the engine knows, it is just another character.&lt;/p&gt;

&lt;p&gt;So everybody learned the ritual: &lt;code&gt;[^\r\n]*\r?$&lt;/code&gt;. It looks like someone sneezed on the keyboard, and as you will see below, it is not even correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet AnyNewLine
&lt;/h2&gt;

&lt;p&gt;.NET 11 adds one flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fixedMatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo\r\nbar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;@".+$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiline&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnyNewLine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedMatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With it, &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;\Z&lt;/code&gt;, and &lt;code&gt;.&lt;/code&gt; recognize all of these as line breaks: &lt;code&gt;\r\n&lt;/code&gt;, &lt;code&gt;\r&lt;/code&gt;, &lt;code&gt;\n&lt;/code&gt;, &lt;code&gt;\u0085&lt;/code&gt; (NEL), &lt;code&gt; &lt;/code&gt; (line separator), and &lt;code&gt; &lt;/code&gt; (paragraph separator). Microsoft's docs say this follows Unicode TR18 RL1.6.&lt;/p&gt;

&lt;p&gt;The important detail is that &lt;code&gt;\r\n&lt;/code&gt; counts as one newline. In &lt;code&gt;"a\r\nb"&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt; matched at positions 1 and 4: before the &lt;code&gt;\r&lt;/code&gt; and at the end, never between &lt;code&gt;\r&lt;/code&gt; and &lt;code&gt;\n&lt;/code&gt;. &lt;code&gt;^&lt;/code&gt; matched at 0 and 3, so the second line starts at &lt;code&gt;b&lt;/code&gt;, not at a stray &lt;code&gt;\n&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic one: a config file from three operating systems
&lt;/h2&gt;

&lt;p&gt;Here is a tiny &lt;code&gt;key=value&lt;/code&gt; parser. The input mixes Windows, Unix, and classic Mac line endings, because somebody's editor had opinions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"host=db.local\r\nport=5432\nuser=app\rtimeout=30\r\n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Line&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = [&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;GeneratedRegex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"^(?&amp;lt;key&amp;gt;\w+)=(?&amp;lt;value&amp;gt;.*)$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiline&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;RegexOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnyNewLine&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="n"&gt;Regex&lt;/span&gt; &lt;span class="nf"&gt;Line&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host = [db.local]
port = [5432]
user = [app]
timeout = [30]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four keys, clean values. The source generator accepts the new flag, and so does &lt;code&gt;RegexOptions.Compiled&lt;/code&gt;, which returned the same four values.&lt;/p&gt;

&lt;p&gt;Now the same pattern without &lt;code&gt;AnyNewLine&lt;/code&gt;. It found only three keys: &lt;code&gt;host&lt;/code&gt; came back as &lt;code&gt;db.local\r&lt;/code&gt;, and &lt;code&gt;user&lt;/code&gt; swallowed the next line whole, as &lt;code&gt;app\rtimeout=30\r&lt;/code&gt;. &lt;code&gt;timeout&lt;/code&gt; vanished, because to the old engine a lone &lt;code&gt;\r&lt;/code&gt; is not a line break at all.&lt;/p&gt;

&lt;p&gt;And the ritual? &lt;code&gt;^(?&amp;lt;key&amp;gt;\w+)=(?&amp;lt;value&amp;gt;[^\r\n]*)\r?$&lt;/code&gt; found 2 of the 4 keys. It fixes &lt;code&gt;\r\n&lt;/code&gt; and quietly gives up on a bare &lt;code&gt;\r&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I use it?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RegexOptions.AnyNewLine&lt;/code&gt; is new in .NET 11, with the value &lt;code&gt;2048&lt;/code&gt;. The .NET 11 Preview 3 release notes introduced it, and the RegexOptions docs list it as available in .NET 11 and later.&lt;/li&gt;
&lt;li&gt;It works with &lt;code&gt;new Regex(...)&lt;/code&gt;, the static &lt;code&gt;Regex&lt;/code&gt; methods, &lt;code&gt;RegexOptions.Compiled&lt;/code&gt;, and &lt;code&gt;[GeneratedRegex]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;.NET 11 RC2 shipped on October 13, 2026 with a go-live support license. It is still a release candidate, and general availability is expected in November.&lt;/li&gt;
&lt;li&gt;On the .NET 10 SDK, the code fails to build with &lt;code&gt;CS0117: 'RegexOptions' does not contain a definition for 'AnyNewLine'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ran every snippet here as a file-based app with &lt;code&gt;dotnet run file.cs&lt;/code&gt; on SDK 11.0.100-rc.1, on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. NonBacktracking and ECMAScript say no.&lt;/strong&gt; &lt;code&gt;RegexOptions.AnyNewLine | RegexOptions.NonBacktracking&lt;/code&gt; threw &lt;code&gt;ArgumentOutOfRangeException&lt;/code&gt; at construction, and so did the combination with &lt;code&gt;ECMAScript&lt;/code&gt;. If you picked &lt;code&gt;NonBacktracking&lt;/code&gt; for its linear-time guarantee, you keep &lt;code&gt;\r?&lt;/code&gt; for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. There is no inline letter.&lt;/strong&gt; &lt;code&gt;Multiline&lt;/code&gt; has &lt;code&gt;(?m)&lt;/code&gt; and &lt;code&gt;Singleline&lt;/code&gt; has &lt;code&gt;(?s)&lt;/code&gt;, but the docs list no inline option for &lt;code&gt;AnyNewLine&lt;/code&gt;. You have to pass the flag in code, so a pattern stored in a config file cannot turn it on by itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Character classes are not newline-aware.&lt;/strong&gt; The flag changes &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;\Z&lt;/code&gt;, and &lt;code&gt;.&lt;/code&gt;, and nothing else. &lt;code&gt;[^\n]+&lt;/code&gt; against &lt;code&gt;"foo\r\nbar"&lt;/code&gt; still matched 4 characters, &lt;code&gt;\r&lt;/code&gt; included, with &lt;code&gt;AnyNewLine&lt;/code&gt; on. If you want "anything but a line break", use &lt;code&gt;.&lt;/code&gt; and let the flag do its job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;\A&lt;/code&gt; and &lt;code&gt;\z&lt;/code&gt; do not move.&lt;/strong&gt; They still mean the absolute start and end of the string. With &lt;code&gt;AnyNewLine&lt;/code&gt;, &lt;code&gt;done\Z&lt;/code&gt; matched &lt;code&gt;"done\r\n"&lt;/code&gt;, while &lt;code&gt;done\z&lt;/code&gt; did not. That is by design, and it is the escape hatch when you really do mean "end of input".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Singleline still wins.&lt;/strong&gt; With &lt;code&gt;Singleline | AnyNewLine&lt;/code&gt;, &lt;code&gt;a.+&lt;/code&gt; against &lt;code&gt;"a\r\nb"&lt;/code&gt; matched the whole string, newline included. &lt;code&gt;Singleline&lt;/code&gt; means "the dot matches everything", and the new flag does not argue with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The docs disagree about &lt;code&gt;\v&lt;/code&gt; and &lt;code&gt;\f&lt;/code&gt;.&lt;/strong&gt; The RegexOptions API reference says &lt;code&gt;AnyNewLine&lt;/code&gt; also recognizes vertical tab and form feed. The options article lists only the six sequences above. In the RC1 run, &lt;code&gt;.&lt;/code&gt; did not match &lt;code&gt;\v&lt;/code&gt; or &lt;code&gt;\f&lt;/code&gt;, and &lt;code&gt;^\w$&lt;/code&gt; found 2 lines in &lt;code&gt;"a\vb"&lt;/code&gt;, so the API reference matches what the runtime did. If your input can contain form feeds, test it rather than trusting either page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\r&lt;/code&gt; has been hiding at the end of captured strings for as long as .NET has had regex. Now one flag tells the engine what a line actually is, on every OS at once. Go search your codebase for &lt;code&gt;\r?$&lt;/code&gt; and see how many of them can retire in .NET 11.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/core/whats-new/dotnet-11/libraries" rel="noopener noreferrer"&gt;What's new in .NET libraries for .NET 11: AnyNewLine option (Microsoft Learn)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regexoptions?view=net-11.0" rel="noopener noreferrer"&gt;RegexOptions enum (Microsoft Learn)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/standard/base-types/regular-expression-options#anynewline-mode" rel="noopener noreferrer"&gt;Regular expression options: AnyNewLine mode (Microsoft Learn)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview3/libraries.md#regex-recognizes-all-unicode-newline-sequences" rel="noopener noreferrer"&gt;.NET 11 Preview 3 libraries release notes (GitHub, dotnet/core)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/dotnet/dotnet-11-rc-2/" rel="noopener noreferrer"&gt;Announcing .NET 11 Release Candidate 2 (.NET Blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unicode.org/reports/tr18/#RL1.6" rel="noopener noreferrer"&gt;Unicode Technical Standard #18, RL1.6 Line Boundaries (unicode.org)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article was written with the help of AI and checked against the sources above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>regex</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Gap-free invoice numbers in SQL Server (and why IDENTITY can't give you them)</title>
      <dc:creator>Zohaib Ahmad</dc:creator>
      <pubDate>Wed, 28 Oct 2026 04:34:49 +0000</pubDate>
      <link>https://dev.to/zohaibahmad/gap-free-invoice-numbers-in-sql-server-and-why-identity-cant-give-you-them-3e25</link>
      <guid>https://dev.to/zohaibahmad/gap-free-invoice-numbers-in-sql-server-and-why-identity-cant-give-you-them-3e25</guid>
      <description>&lt;p&gt;Accountants care about receipt and invoice numbers more than developers expect. If they see S-10481, S-10482, S-10484, the first question is: &lt;em&gt;who deleted S-10483?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The usual tools don't help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IDENTITY&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;SEQUENCE&lt;/code&gt;&lt;/strong&gt; hand out a value and never take it back. When the transaction rolls back (card declined, stock check failed, a timeout), the number is gone and you have a gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MAX(Number) + 1&lt;/code&gt;&lt;/strong&gt; gives two simultaneous sales the same number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how &lt;a href="https://studioyume.pages.dev/pos/" rel="noopener noreferrer"&gt;Tilly POS&lt;/a&gt; does it. It's an excerpt from my ebook, &lt;em&gt;Building a Production POS in ASP.NET Core&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A counter row, updated inside the sale's transaction
&lt;/h2&gt;

&lt;p&gt;One small table holds the next value per business and per sequence name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;NumberSequences&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="n"&gt;uniqueidentifier&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;      &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NextValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;           &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PK_NumberSequences&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reserving a number is one locked &lt;code&gt;UPDATE&lt;/code&gt; that returns the old value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ReserveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reserved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SqlQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt;            &lt;span class="n"&gt;UPDATE&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;NumberSequences&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UPDLOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HOLDLOCK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;SET&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NextValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NextValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;OUTPUT&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="n"&gt;NextValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="s"&gt;""").ToListAsync(ct);
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reserved&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteSqlAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt;                &lt;span class="n"&gt;INSERT&lt;/span&gt; &lt;span class="n"&gt;INTO&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;NumberSequences&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NextValue&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="s"&gt;""", ct);
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SqlException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="m"&gt;2627&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="m"&gt;2601&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Another request created the row first; loop round and take the UPDATE path.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The interpolated values are parameters, not string concatenation — &lt;code&gt;SqlQuery&lt;/code&gt; and &lt;code&gt;ExecuteSqlAsync&lt;/code&gt; take a &lt;code&gt;FormattableString&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it has no gaps
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;UPDATE&lt;/code&gt; runs &lt;strong&gt;inside the same transaction as the sale&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransactionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"S-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sequences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReserveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SequenceNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... payments, stock movements, audit entry&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If the sale fails, the transaction rolls back — &lt;strong&gt;and so does the counter&lt;/strong&gt;. The next sale gets the same number the failed one would have had.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UPDLOCK&lt;/code&gt; makes a second sale for the same business wait at the counter row until the first commits or rolls back, so two sales can never read the same value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HOLDLOCK&lt;/code&gt; keeps the lock to the end of the transaction, so the "no row yet" case can't race either; if two first-ever sales both try to &lt;code&gt;INSERT&lt;/code&gt;, one hits the primary key and simply goes round to the &lt;code&gt;UPDATE&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The cost, and why it's fine
&lt;/h2&gt;

&lt;p&gt;Sales for &lt;strong&gt;one business&lt;/strong&gt; now number themselves one at a time. The lock is held only from the reservation to the commit — a few milliseconds — and different businesses never wait for each other, because the key includes the tenant.&lt;/p&gt;

&lt;p&gt;For a point of sale, invoicing or anything an accountant reads, that's exactly the right trade: correctness at the cost of a tiny, local queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  More like this
&lt;/h2&gt;

&lt;p&gt;The ebook covers the rest of the checkout the same way: idempotency keys against double charges, a conditional &lt;code&gt;UPDATE&lt;/code&gt; that stops two tills selling the last unit, stock as a ledger, multi-tenancy, refresh-token rotation and an append-only audit log — 23 pages with the real code. &lt;strong&gt;&lt;a href="https://daydreamer6550.gumroad.com/l/production-pos-ebook" rel="noopener noreferrer"&gt;Building a Production POS in ASP.NET Core&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>sqlserver</category>
      <category>dotnet</category>
      <category>database</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Multi-tenancy in EF Core: make "no tenant" mean "no rows"</title>
      <dc:creator>Zohaib Ahmad</dc:creator>
      <pubDate>Wed, 28 Oct 2026 04:33:48 +0000</pubDate>
      <link>https://dev.to/zohaibahmad/multi-tenancy-in-ef-core-make-no-tenant-mean-no-rows-5770</link>
      <guid>https://dev.to/zohaibahmad/multi-tenancy-in-ef-core-make-no-tenant-mean-no-rows-5770</guid>
      <description>&lt;p&gt;The worst bug a SaaS can ship is one customer seeing another customer's data. If your isolation depends on every developer remembering &lt;code&gt;.Where(x =&amp;gt; x.TenantId == tenantId)&lt;/code&gt; on every query, it's not a matter of &lt;em&gt;if&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's the pattern I use in ASP.NET Core and EF Core, taken from a production point of sale and now the foundation of my &lt;a href="https://daydreamer6550.gumroad.com/l/saas-starter-kit" rel="noopener noreferrer"&gt;SaaS Starter Kit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. One base class for everything a tenant owns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ITenantOwned&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TenantEntity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ITenantOwned&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. A query filter on every tenant-owned table
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;DbContext&lt;/code&gt; gets the current user injected, and every tenant-owned entity gets a global query filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICurrentUser&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IdentityUserContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// EF Core evaluates this per query, so each request sees only its own tenant.&lt;/span&gt;
    &lt;span class="c1"&gt;// No tenant (anonymous or background code) means filtered sets return nothing.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;CurrentTenantId&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"customers"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Phone&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;HasFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[Phone] IS NOT NULL"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasOne&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Tenant&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithMany&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;HasForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;OnDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DeleteBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Restrict&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;CurrentTenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;db.Customers.ToListAsync()&lt;/code&gt; only ever returns the signed-in business's customers, everywhere, without anyone writing a &lt;code&gt;Where&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The detail that makes it safe: the default is &lt;em&gt;nothing&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CurrentTenantId&lt;/code&gt; is &lt;code&gt;Guid?&lt;/code&gt;. When nobody is signed in, it's &lt;code&gt;null&lt;/code&gt; — and &lt;code&gt;c.TenantId == null&lt;/code&gt; matches no rows.&lt;/p&gt;

&lt;p&gt;That means the failure mode of a forgotten &lt;code&gt;[Authorize]&lt;/code&gt; or a background job without a tenant is an &lt;strong&gt;empty list&lt;/strong&gt;, not every business's data. Fail closed, not open.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Unique indexes include the tenant
&lt;/h2&gt;

&lt;p&gt;A phone number should be unique &lt;em&gt;within a business&lt;/em&gt;, not across the whole platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Phone&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;HasFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[Phone] IS NOT NULL"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two shops can both have a customer on 0300 1234567; one shop can't create the same customer twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keep &lt;code&gt;IgnoreQueryFilters()&lt;/code&gt; rare and greppable
&lt;/h2&gt;

&lt;p&gt;Some code genuinely runs before there's a tenant: sign-in (you're looking up the user to &lt;em&gt;find&lt;/em&gt; their tenant), self sign-up, and platform jobs like billing webhooks. Those are the only places that call &lt;code&gt;IgnoreQueryFilters()&lt;/code&gt;, and code review checks each one. If you see it in a feature module, that's a bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Prove it with one test
&lt;/h2&gt;

&lt;p&gt;The most valuable test in my suite creates two businesses, has A create records, and has B try to read and change them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;No_record_of_one_business_can_be_read_or_changed_by_another&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTenantAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateTenantAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ownerA&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SignInOwnerAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ownerB&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SignInOwnerAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;IdOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ownerA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ali"&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ownerB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/api/v1/customers/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ownerB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PutAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/api/v1/customers/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hacked"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;False&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs against a real SQL Server (LocalDB locally, a container in CI). When someone adds an entity and forgets the filter, this is the test that catches it — before a customer does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the whole thing?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://daydreamer6550.gumroad.com/l/saas-starter-kit" rel="noopener noreferrer"&gt;SaaS Starter Kit&lt;/a&gt; is this pattern plus everything around it: self sign-up into a trial, JWT with rotating refresh tokens, roles and permissions, plans with server-enforced limits, a signed billing webhook, an audit log, Docker, CI and 31 integration tests — ASP.NET Core 10 and Angular 22.&lt;/p&gt;

&lt;p&gt;How do you isolate tenants — filters, schemas, or a database per tenant? I'd like to hear in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
      <category>saas</category>
      <category>csharp</category>
    </item>
    <item>
      <title>I built a multi-branch point of sale with ASP.NET Core 10 and Angular. Here's the tour.</title>
      <dc:creator>Zohaib Ahmad</dc:creator>
      <pubDate>Wed, 28 Oct 2026 04:28:39 +0000</pubDate>
      <link>https://dev.to/zohaibahmad/i-built-a-multi-branch-point-of-sale-with-aspnet-core-10-and-angular-heres-the-tour-32b5</link>
      <guid>https://dev.to/zohaibahmad/i-built-a-multi-branch-point-of-sale-with-aspnet-core-10-and-angular-heres-the-tour-32b5</guid>
      <description>&lt;p&gt;For the last few months I've been building &lt;strong&gt;Tilly POS&lt;/strong&gt;, a point of sale and back office for shops and small retail chains. It's live, there's a demo you can break, and this post is the tour: what it does, how it's put together, and what I'd tell anyone building something similar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it first:&lt;/strong&gt; &lt;a href="https://yume-pos.pages.dev/demo/cashier" rel="noopener noreferrer"&gt;open the demo as the cashier&lt;/a&gt; or &lt;a href="https://yume-pos.pages.dev/demo/owner" rel="noopener noreferrer"&gt;as the owner&lt;/a&gt;. No sign-up; it resets every night.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checkout&lt;/strong&gt; — scan or search, held sales, discounts with manager approval, split payments, receipts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cash drawer&lt;/strong&gt; — shifts with an opening float, paid in/out, a denomination count at close, manager review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Returns and voids&lt;/strong&gt; — partial refunds to the original payment method&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stock in every branch&lt;/strong&gt; — a ledger per item, weighted-average cost, counts, adjustments, transfers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purchasing&lt;/strong&gt; — suppliers, purchase orders, goods received&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports&lt;/strong&gt; — sales, profit, stock, purchases and cash, with CSV export&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Administration&lt;/strong&gt; — users, custom roles, branches and registers, and an audit log nobody can edit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;ASP.NET Core 10 minimal APIs&lt;/td&gt;
&lt;td&gt;Small handlers, typed results, accurate OpenAPI for free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;EF Core + SQL Server&lt;/td&gt;
&lt;td&gt;Transactions and row locks are the whole game in a POS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Front end&lt;/td&gt;
&lt;td&gt;Angular 22 (standalone, signals) + Tailwind v4&lt;/td&gt;
&lt;td&gt;Fast, keyboard-first checkout screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Docker Compose (SQL Server + API + nginx)&lt;/td&gt;
&lt;td&gt;One command on a small VPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests&lt;/td&gt;
&lt;td&gt;xUnit + &lt;code&gt;WebApplicationFactory&lt;/code&gt; against a real SQL Server&lt;/td&gt;
&lt;td&gt;104 integration tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The back end is a &lt;strong&gt;modular monolith&lt;/strong&gt;: Identity, Tenancy, Catalog, Inventory, Sales, Registers, Purchasing, Reports, Billing and Audit are separate folders with their own endpoints and their own SQL schema, but one process and one database. A sale writes to sales, payments, stock and audit — and in one database that's one transaction instead of a distributed saga.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three decisions I'd make again
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Idempotent checkout.&lt;/strong&gt; The browser sends a key with every sale attempt; a unique index on (business, key) makes a retried &lt;strong&gt;Charge&lt;/strong&gt; return the original sale instead of charging twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The stock check &lt;em&gt;is&lt;/em&gt; the write.&lt;/strong&gt; "Is there enough?" is a conditional, row-locked &lt;code&gt;UPDATE&lt;/code&gt; — if no row comes back, the sale is refused. A read-then-write check lets two tills sell the last unit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Read-only, not locked out.&lt;/strong&gt; When a trial ends or a payment fails, the business can still see and export everything; it just can't change anything. Nobody loses access to their own records over a billing problem.&lt;/p&gt;

&lt;p&gt;I wrote up the first two in detail, with code: &lt;a href="https://dev.to/zohaibahmad/three-bugs-a-point-of-sale-cant-afford-and-how-i-designed-them-out-in-aspnet-core-21j1"&gt;Three bugs a point-of-sale can't afford&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you if you're building one
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test against the real database.&lt;/strong&gt; Every bug that matters in a POS lives in transactions, locks and unique indexes. An in-memory provider passes and proves nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for the bad Wi-Fi day,&lt;/strong&gt; not the demo. Retries, double clicks and half-finished requests are normal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the cashier's error messages say what happened and what to do.&lt;/strong&gt; "Only 0 of 'Basmati rice 5 kg' left in this branch. Change the quantity or remove the item; nothing was charged." beats "Error 409".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Shops can use it as a monthly service: &lt;a href="https://studioyume.pages.dev/pos/" rel="noopener noreferrer"&gt;studioyume.pages.dev/pos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Developers and agencies can buy the full source code (Docker setup, docs and all 104 tests): &lt;a href="https://daydreamer6550.gumroad.com/l/yume-pos-source-code" rel="noopener noreferrer"&gt;Tilly POS on Gumroad&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions about any of it — the architecture, the checkout, the stock ledger — are very welcome in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>angular</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cómo recibo webhooks de Mercado Pago en ASP.NET Core</title>
      <dc:creator>Martin Pisacreta</dc:creator>
      <pubDate>Wed, 28 Oct 2026 01:54:54 +0000</pubDate>
      <link>https://dev.to/martinpisacreta/como-recibo-webhooks-de-mercado-pago-en-aspnet-core-4j77</link>
      <guid>https://dev.to/martinpisacreta/como-recibo-webhooks-de-mercado-pago-en-aspnet-core-4j77</guid>
      <description>&lt;p&gt;Una notificación de pago puede llegar más de una vez. También puede aparecer cuando mi aplicación ya recibió una actualización más reciente. Si cada entrega dispara otra acreditación, un reintento puede convertirse en un problema de dinero y conciliación.&lt;/p&gt;

&lt;p&gt;Preparé una muestra propia en ASP.NET Core 8 para explorar ese flujo: autenticar la notificación, consultar el pago y actualizar una orden sin repetir trabajo ante una entrega idéntica. Sus ocho pruebas pasan en Windows sobre .NET 8, con la compilación Release sin advertencias ni errores. La muestra utiliza memoria y una API simulada; todavía no demuestra una integración real con Mercado Pago.&lt;/p&gt;

&lt;p&gt;Estos son el recorrido, las comprobaciones y los límites que mantengo visibles antes de adaptar el ejemplo a un negocio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separar la notificación del estado del pago
&lt;/h2&gt;

&lt;p&gt;Mi endpoint recibe un identificador, pero no toma el estado del pago ni la orden asociada del cuerpo de la notificación. Esos datos deben venir de una consulta autenticada a la API de pagos.&lt;/p&gt;

&lt;p&gt;La interfaz &lt;code&gt;IPaymentsApi&lt;/code&gt; devuelve el identificador del pago, la orden local verificada, el estado y la fecha de actualización. El adaptador debe resolver la referencia del proveedor contra mis registros. Una firma correcta no reemplaza esa comprobación de negocio.&lt;/p&gt;

&lt;p&gt;La muestra no incluye ese adaptador real. Registra una implementación que devuelve un error y responde 503 ante una notificación autenticada. Para usarla con pagos reales, primero tendría que implementar y validar la consulta con credenciales guardadas fuera del código.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validar la entrada antes de procesarla
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;POST /mercadopago/webhook&lt;/code&gt; acepta &lt;code&gt;data.id&lt;/code&gt; desde la consulta o desde un objeto JSON. Si ambas ubicaciones incluyen el identificador, exige que coincidan exactamente. Una discrepancia produce 400 en lugar de elegir silenciosamente uno de los valores.&lt;/p&gt;

&lt;p&gt;También exige un solo encabezado &lt;code&gt;x-request-id&lt;/code&gt; y uno &lt;code&gt;x-signature&lt;/code&gt;. El procesador interpreta una firma con componentes &lt;code&gt;ts&lt;/code&gt; y &lt;code&gt;v1&lt;/code&gt;, rechaza componentes desconocidos o repetidos y comprueba que el resumen hexadecimal tenga la longitud esperada.&lt;/p&gt;

&lt;p&gt;Son reglas concretas de esta implementación. No presento el ejemplo como compatible con cualquier variante de notificación: esa compatibilidad sigue necesitando pruebas con entregas reales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalizar el identificador dentro del manifiesto
&lt;/h2&gt;

&lt;p&gt;La firma se calcula sobre un manifiesto, no sobre todo el JSON recibido. En esta muestra, el valor de &lt;code&gt;data.id&lt;/code&gt; se convierte a minúsculas al construir ese texto. El identificador de solicitud conserva sus mayúsculas y minúsculas.&lt;/p&gt;

&lt;p&gt;Este es el primer fragmento de mi archivo &lt;code&gt;Webhooks.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"id:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaymentId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLowerInvariant&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s"&gt;;request-id:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;;ts:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HMACSHA256&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HashData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CryptographicOperations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FixedTimeEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;supplied&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El punto y coma final forma parte del manifiesto. Uso UTF-8, HMAC-SHA256 y comparación de los bytes en tiempo constante. El secreto de webhooks es distinto del token utilizado para consultar pagos.&lt;/p&gt;

&lt;p&gt;La normalización no modifica el identificador que envío al adaptador ni el que guardo en la orden. El octavo caso usa &lt;code&gt;ABC123XYZ&lt;/code&gt;, calcula la firma independientemente con &lt;code&gt;abc123xyz&lt;/code&gt; y conserva &lt;code&gt;MixedCaseRequest8&lt;/code&gt; como identificador de solicitud. Así comprueba que solo se normalice el valor destinado al manifiesto.&lt;/p&gt;

&lt;p&gt;El procesador también exige que la marca de tiempo esté dentro de una tolerancia configurable, de cinco minutos por defecto hacia el pasado o el futuro. El reloj es inyectable para probar ese comportamiento sin depender de la hora de ejecución.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconocer una repetición exacta
&lt;/h2&gt;

&lt;p&gt;Después de autenticar, el procesador adquiere un semáforo y revisa un conjunto de manifiestos procesados. Este segundo fragmento muestra la salida temprana:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"duplicate"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una repetición exacta dentro de la ventana válida recibe 200 sin volver a consultar la API. La autenticación ocurre antes: una entrega vencida recibe 401 aunque su manifiesto haya sido procesado previamente.&lt;/p&gt;

&lt;p&gt;Si cambia el identificador de solicitud o la marca de tiempo, cambia la clave y se consulta nuevamente el pago. Eso permite observar actualizaciones posteriores del mismo pago, como el paso de pendiente a aprobado. Deduplicar permanentemente solo por identificador de pago impediría aplicar esa transición.&lt;/p&gt;

&lt;p&gt;Cuando el adaptador falla, respondo 503 sin registrar el manifiesto como procesado. De esa manera, el intento fallido no bloquea una entrega posterior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evitar que una actualización vieja haga retroceder la orden
&lt;/h2&gt;

&lt;p&gt;Una entrega nueva puede traer una consulta cuyo resultado sea anterior al estado guardado. Comparo la fecha de actualización obtenida de la API, no la hora de llegada del webhook ni la marca de su firma.&lt;/p&gt;

&lt;p&gt;El tercer fragmento contiene las reglas de retroceso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wouldRegress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"approved"&lt;/span&gt;
        &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="s"&gt;"pending"&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="s"&gt;"rejected"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"refunded"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"refunded"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un resultado anterior o una transición prohibida recibe 200 con &lt;code&gt;ignored_stale_or_regressive&lt;/code&gt;. La orden conserva su estado. Un resultado actual o posterior puede llevar una aprobación a reembolso; una orden reembolsada no vuelve a otro estado con estas reglas.&lt;/p&gt;

&lt;p&gt;La muestra admite un pago por orden. Si aparece otro identificador para una orden ya asociada, responde 409. Es una decisión del ejemplo que habría que revisar para negocios con varios intentos de pago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué cubren las ocho pruebas
&lt;/h2&gt;

&lt;p&gt;Las ocho pruebas pasan en Windows sobre .NET 8, con la compilación Release sin advertencias ni errores.&lt;/p&gt;

&lt;p&gt;Los casos cubren pago aprobado, firma incorrecta, firma vencida, notificación duplicada, transición de pendiente a aprobado, pago rechazado, aprobación seguida de un resultado pendiente antiguo y normalización del identificador alfanumérico.&lt;/p&gt;

&lt;p&gt;El ejecutor llama directamente al procesador con un reloj fijo y una API falsa que cuenta consultas. El caso duplicado verifica una sola consulta después de dos entregas. Los casos de firma inválida o vencida comprueban que no se consulte la API ni se modifique la orden.&lt;/p&gt;

&lt;p&gt;No se inicia un servidor HTTP ni se realizan llamadas externas. Tampoco hay pruebas específicas de reembolsos, concurrencia distribuida o recuperación después de un reinicio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué falta para una acreditación durable
&lt;/h2&gt;

&lt;p&gt;Las órdenes y las claves procesadas viven en memoria. Un reinicio borra ambas, y el conjunto de claves crece sin una política de retención. El semáforo coordina una instancia y mantiene serializadas incluso las consultas al adaptador; no coordina varios procesos.&lt;/p&gt;

&lt;p&gt;Además, actualizar un estado no equivale a acreditar dinero una sola vez. El ejemplo no ejecuta una acreditación contable, un envío ni una transferencia. Para incorporar esos efectos necesitaría almacenamiento durable, restricciones únicas y una transacción que vincule la protección contra duplicados con el cambio de negocio. Las acciones externas requieren su propia estrategia de recuperación.&lt;/p&gt;

&lt;p&gt;Los tres fragmentos pertenecen a mi muestra propia, sin repositorio público. La considero una base verificable para discutir el flujo, con integración real y persistencia todavía pendientes.&lt;/p&gt;

&lt;p&gt;Si necesitas recibir webhooks de Mercado Pago en tu API de ASP.NET Core, escríbeme sobre tu flujo de pagos y los reintentos que necesitas resolver.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>webdev</category>
      <category>spanish</category>
    </item>
  </channel>
</rss>
