<?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: Rohit Kori</title>
    <description>The latest articles on DEV Community by Rohit Kori (@brawnybytes).</description>
    <link>https://dev.to/brawnybytes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2565213%2Fc9430d99-fe5f-4d2a-a327-dcf8c90b70cd.jpg</url>
      <title>DEV Community: Rohit Kori</title>
      <link>https://dev.to/brawnybytes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brawnybytes"/>
    <language>en</language>
    <item>
      <title>Designing an E-Commerce System</title>
      <dc:creator>Rohit Kori</dc:creator>
      <pubDate>Mon, 10 Aug 2026 17:28:08 +0000</pubDate>
      <link>https://dev.to/brawnybytes/designing-an-e-commerce-system-54mi</link>
      <guid>https://dev.to/brawnybytes/designing-an-e-commerce-system-54mi</guid>
      <description>&lt;p&gt;Imagine opening Amazon or Flipkart during a major sale.&lt;/p&gt;

&lt;p&gt;You search for a product, open its page, add it to your cart, choose an address, pay for the order, and eventually receive the package.&lt;/p&gt;

&lt;p&gt;From the customer's perspective, the experience is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search
  ↓
Product
  ↓
Cart
  ↓
Checkout
  ↓
Payment
  ↓
Order
  ↓
Delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, however, many independent systems are working together.&lt;/p&gt;

&lt;p&gt;The platform has to search through hundreds of millions of products, serve millions of product-page requests, keep carts available across devices, maintain accurate inventory, process payments, create orders, communicate with warehouses, and survive huge traffic spikes during events such as Black Friday or Prime Day.&lt;/p&gt;

&lt;p&gt;The most interesting part of the design is not the product catalog itself. The difficult problems appear when &lt;strong&gt;large traffic, concurrency, inventory, payment, and distributed services meet each other&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article walks through the system from the beginning and gradually introduces those problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Understanding the Requirements
&lt;/h1&gt;

&lt;p&gt;Let's start with the customer experience.&lt;/p&gt;

&lt;p&gt;A customer should be able to browse the product catalog and search for products using text and filters.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"wireless headphones"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The customer might then filter by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Brand = Sony
Price = ₹5,000–₹30,000
Rating &amp;gt; 4
Category = Headphones
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After finding a product, the customer should be able to view its details, add it to a cart, and proceed to checkout.&lt;/p&gt;

&lt;p&gt;During checkout, the system needs to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validate the cart
       ↓
Check the current price
       ↓
Check inventory
       ↓
Reserve inventory
       ↓
Calculate the final amount
       ↓
Authorize payment
       ↓
Create the order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the order is created, other systems take over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
  ↓
Fulfillment
  ↓
Shipping
  ↓
Delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The platform also needs to support sellers, inventory management, notifications, recommendations, reviews, and analytics.&lt;/p&gt;

&lt;p&gt;For this design, the most important parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product catalog&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Cart&lt;/li&gt;
&lt;li&gt;Inventory&lt;/li&gt;
&lt;li&gt;Checkout&lt;/li&gt;
&lt;li&gt;Payment&lt;/li&gt;
&lt;li&gt;Order management&lt;/li&gt;
&lt;li&gt;Fulfillment&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The internals of warehouse management, advanced fraud detection, and recommendation algorithms can be treated as separate systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. The First Important Observation
&lt;/h1&gt;

&lt;p&gt;Not every part of an e-commerce platform has the same consistency requirements.&lt;/p&gt;

&lt;p&gt;Consider a product search.&lt;/p&gt;

&lt;p&gt;A customer searches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"iPhone"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is perfectly acceptable if a newly added product takes a few seconds to appear in search.&lt;/p&gt;

&lt;p&gt;Now consider inventory.&lt;/p&gt;

&lt;p&gt;Suppose only one phone remains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stock = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two customers attempt to buy it at nearly the same time.&lt;/p&gt;

&lt;p&gt;The system must not sell two phones.&lt;/p&gt;

&lt;p&gt;This gives us an important distinction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browsing / Search
        ↓
Very high traffic
Eventual consistency is often acceptable

Checkout / Inventory / Payment
        ↓
Correctness is critical
Strong consistency is required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction will influence almost every architectural decision we make.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. How Much Traffic Are We Designing For?
&lt;/h1&gt;

&lt;p&gt;Before choosing databases and services, we need some scale assumptions.&lt;/p&gt;

&lt;p&gt;Suppose our platform has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100M+ registered users
10M+ daily active users
100M–500M products
Millions of orders per day
Tens or hundreds of thousands of search requests per second
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact numbers aren't important. They are assumptions that help us reason about the architecture.&lt;/p&gt;

&lt;p&gt;Suppose we process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10M orders/day
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The average is only around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000,000 / 86,400
≈ 116 orders/sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That number might look manageable.&lt;/p&gt;

&lt;p&gt;But designing for 116 requests/sec would be a mistake.&lt;/p&gt;

&lt;p&gt;Traffic is not evenly distributed.&lt;/p&gt;

&lt;p&gt;During a large sale, the system might suddenly receive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10K+ checkout attempts/sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and search traffic could be much higher.&lt;/p&gt;

&lt;p&gt;So we design for &lt;strong&gt;peak traffic&lt;/strong&gt;, not average traffic.&lt;/p&gt;

&lt;p&gt;This immediately tells us that different workloads need different scaling strategies.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. The High-Level Architecture
&lt;/h1&gt;

&lt;p&gt;A useful first version of the architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         USERS
                           |
                           ▼
                    CDN / API Gateway
                           |
              ┌────────────┼────────────┐
              ↓            ↓            ↓
           Product       Search        Cart
           Service       Service       Service
              |            |            |
              ↓            ↓            ↓
          Product DB   Elasticsearch   Redis
              |
              ↓
             Kafka
              |
       ┌──────┼───────────────┐
       ↓      ↓               ↓
   Inventory  Order        Other Events
       |       |
       ↓       ↓
  Inventory   Order DB
     DB
       |
       ↓
    Checkout
       |
   ┌───┼────────────┐
   ↓   ↓            ↓
Inventory Pricing  Payment
                    |
                    ▼
             Payment Gateway
                    |
                    ▼
                  Order
                    |
                    ▼
                  Kafka
                    |
        ┌───────────┼───────────┐
        ↓           ↓           ↓
   Notification  Warehouse   Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This diagram looks complicated, but the reasoning is straightforward.&lt;/p&gt;

&lt;p&gt;We separate the system according to the workload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product browsing → cache / CDN / search index

Search           → Elasticsearch

Cart             → Redis

Inventory        → transactional database

Orders           → transactional database

Events           → Kafka

Payment          → payment provider

Flash sales      → Redis + queues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's understand why each piece exists.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Building the Product Catalog
&lt;/h1&gt;

&lt;p&gt;The product catalog is the foundation of the platform.&lt;/p&gt;

&lt;p&gt;A product might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;productId
name
description
brand
category
price
images
attributes
rating
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part is that different categories have different attributes.&lt;/p&gt;

&lt;p&gt;A T-shirt might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;size
color
material
fit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A laptop might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU
RAM
storage
screen size
GPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A book might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISBN
author
publisher
language
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the structure isn't identical for every product, a document-oriented database such as MongoDB or DynamoDB can be a reasonable choice for the catalog.&lt;/p&gt;

&lt;p&gt;A simplified document could look like:&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;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sony Headphones"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sony"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Electronics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&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;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Black"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"connectivity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bluetooth"&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;p&gt;The catalog database is the source of truth for product information.&lt;/p&gt;

&lt;p&gt;But we shouldn't use this database directly for every product search.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Why Search Needs Its Own System
&lt;/h1&gt;

&lt;p&gt;Searching a large catalog is a different problem from storing products.&lt;/p&gt;

&lt;p&gt;Suppose the customer searches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wireless headphones
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Brand = Sony
Price = ₹5,000–₹30,000
Rating &amp;gt;= 4
Availability = In stock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A relational or document database can perform simple queries, but at very large scale we want a system specifically optimized for search.&lt;/p&gt;

&lt;p&gt;This is where Elasticsearch comes in.&lt;/p&gt;

&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product Database
       |
       ↓
Product Changed Event
       |
       ↓
     Kafka
       |
       ↓
 Search Indexer
       |
       ↓
 Elasticsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elasticsearch can provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-text search&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Faceted navigation&lt;/li&gt;
&lt;li&gt;Autocomplete&lt;/li&gt;
&lt;li&gt;Fuzzy matching&lt;/li&gt;
&lt;li&gt;Relevance ranking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The customer request then becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
Search API
 ↓
Elasticsearch
 ↓
Search results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps heavy search traffic away from the transactional product database.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Why Search Can Be Eventually Consistent
&lt;/h1&gt;

&lt;p&gt;Suppose a seller creates a new product.&lt;/p&gt;

&lt;p&gt;The product is immediately saved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product DB
    ↓
Product exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then an event is published:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product Created
    ↓
Kafka
    ↓
Search Indexer
    ↓
Elasticsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There may be a short delay.&lt;/p&gt;

&lt;p&gt;For a few seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product DB       → new product exists
Elasticsearch    → product not indexed yet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's usually fine.&lt;/p&gt;

&lt;p&gt;Search doesn't have to be perfectly synchronized every millisecond.&lt;/p&gt;

&lt;p&gt;But checkout is different.&lt;/p&gt;

&lt;p&gt;Suppose Elasticsearch says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Price = ₹50,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the product database says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Price = ₹55,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We cannot let the customer pay based only on the search result.&lt;/p&gt;

&lt;p&gt;Therefore, during checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search result
      ↓
Re-fetch authoritative data
      ↓
Validate current price
      ↓
Validate current inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The search index helps us find the product.&lt;/p&gt;

&lt;p&gt;It does not become the source of truth for money or inventory.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Making Product Reads Fast
&lt;/h1&gt;

&lt;p&gt;Product pages can receive enormous traffic.&lt;/p&gt;

&lt;p&gt;A popular product might be viewed millions of times.&lt;/p&gt;

&lt;p&gt;We don't want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1M requests
     ↓
Product Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, we can cache frequently accessed product information in Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
Product Service
 ↓
Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;product:P123

name = iPhone
price = ₹80,000
brand = Apple
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the value exists in Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis → return immediately
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it doesn't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis miss
    ↓
Product DB
    ↓
Store in Redis
    ↓
Return
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic cache-aside pattern.&lt;/p&gt;

&lt;p&gt;The database remains authoritative.&lt;/p&gt;

&lt;p&gt;Redis exists primarily to make common reads fast and protect the database from excessive read traffic.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Designing the Shopping Cart
&lt;/h1&gt;

&lt;p&gt;Now the customer adds a product to the cart.&lt;/p&gt;

&lt;p&gt;A cart might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cart
-------------------------
iPhone       × 1
Headphones   × 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a logged-in customer, Redis is a good fit because cart operations are frequent and latency-sensitive.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cart:user123

iPhone       → 1
Headphones   → 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A TTL can eventually remove abandoned carts.&lt;/p&gt;

&lt;p&gt;Guest users are slightly different.&lt;/p&gt;

&lt;p&gt;A guest cart can initially live in the browser. When the user logs in, the guest cart can be merged with the user's server-side cart.&lt;/p&gt;

&lt;p&gt;The important thing is that the cart should not be treated as the final source of truth for price or inventory.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. The Cart Price Problem
&lt;/h1&gt;

&lt;p&gt;Suppose a customer adds headphones for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;₹10,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to the cart.&lt;/p&gt;

&lt;p&gt;Two hours later, the seller changes the price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;₹12,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What should happen?&lt;/p&gt;

&lt;p&gt;The cart might still display the old price.&lt;/p&gt;

&lt;p&gt;But when checkout begins, we must revalidate it.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cart
 ↓
Fetch current product price
 ↓
Fetch current promotions
 ↓
Calculate current total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server, not the browser, determines the final amount.&lt;/p&gt;

&lt;p&gt;A customer should never be able to send:&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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and expect the server to charge ₹1 for a ₹10,000 product.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Inventory Is Where the Design Gets Interesting
&lt;/h1&gt;

&lt;p&gt;Inventory is one of the hardest parts of an e-commerce system because of concurrency.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iPhone stock = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At exactly the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → BUY
User B → BUY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both requests could read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stock = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we simply perform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read stock
 ↓
Check stock
 ↓
Decrease stock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;both requests could succeed.&lt;/p&gt;

&lt;p&gt;The result would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Two customers
      ↓
Two orders
      ↓
One physical phone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's overselling.&lt;/p&gt;

&lt;p&gt;We need an atomic operation.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. A Simple Inventory Solution: Database Locking
&lt;/h1&gt;

&lt;p&gt;One approach is a database transaction with a row lock.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- check quantity&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first transaction locks the inventory row.&lt;/p&gt;

&lt;p&gt;If stock is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the first buyer reserves it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 → 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the second buyer gets the lock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 → reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is easy to reason about and gives strong consistency.&lt;/p&gt;

&lt;p&gt;But there is a problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  13. Why Database Locking Isn't Enough for Flash Sales
&lt;/h1&gt;

&lt;p&gt;Imagine a flash sale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stock = 1,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;all trying to buy the same product.&lt;/p&gt;

&lt;p&gt;Now one database row becomes extremely hot.&lt;/p&gt;

&lt;p&gt;Thousands of requests may compete for the same lock.&lt;/p&gt;

&lt;p&gt;We can get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High lock contention
Slow transactions
Connection pool exhaustion
Database CPU pressure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we need a different strategy for extreme traffic.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. Atomic Inventory Updates
&lt;/h1&gt;

&lt;p&gt;For normal high-volume traffic, we can often avoid explicit locks by making the database update itself atomic.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rows affected = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the reservation succeeded.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rows affected = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;there was no inventory available.&lt;/p&gt;

&lt;p&gt;The important part is that the database performs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check quantity
+
Decrease quantity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as one atomic operation.&lt;/p&gt;

&lt;p&gt;This is often a better starting point than pessimistic locking.&lt;/p&gt;




&lt;h1&gt;
  
  
  15. Inventory Reservations
&lt;/h1&gt;

&lt;p&gt;There is another important concept: we shouldn't necessarily permanently consume inventory as soon as a customer starts checkout.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stock = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A customer starts checkout.&lt;/p&gt;

&lt;p&gt;We reserve one item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available = 9
Reserved = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The customer now has, for example, 15 minutes to complete payment.&lt;/p&gt;

&lt;p&gt;If payment succeeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reserved → Sold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the customer abandons checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reservation expires
      ↓
Reserved → Available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents customers from holding inventory forever.&lt;/p&gt;

&lt;p&gt;The reservation lifecycle can be represented as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AVAILABLE
    ↓
RESERVED
    ↓
    ├── PAYMENT SUCCESS → SOLD
    |
    └── TIMEOUT → AVAILABLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  16. Flash Sales Require a Different Approach
&lt;/h1&gt;

&lt;p&gt;Now consider a limited product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000 units
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We cannot allow all million requests to hit the inventory database.&lt;/p&gt;

&lt;p&gt;Instead, Redis can act as a very fast admission-control layer.&lt;/p&gt;

&lt;p&gt;Before the sale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flash:stock:P123 = 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A purchase attempts an atomic decrement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECR flash:stock:P123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis can process these operations extremely quickly.&lt;/p&gt;

&lt;p&gt;Once the counter reaches zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sold out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Redis should not become our only permanent source of truth.&lt;/p&gt;

&lt;p&gt;A better architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Millions of users
       ↓
Rate Limiter
       ↓
Queue
       ↓
Redis atomic stock counter
       ↓
Successful buyers
       ↓
Order workers
       ↓
Durable inventory database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis protects the database from the enormous burst.&lt;/p&gt;

&lt;p&gt;The database remains the durable record.&lt;/p&gt;

&lt;p&gt;A reconciliation process can compare Redis and the database and repair any inconsistencies.&lt;/p&gt;




&lt;h1&gt;
  
  
  17. Protecting the System With a Queue
&lt;/h1&gt;

&lt;p&gt;Even Redis can receive a huge number of requests.&lt;/p&gt;

&lt;p&gt;A queue gives the system backpressure.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1M users
   ↓
1M synchronous requests
   ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1M users
   ↓
Queue
   ↓
Controlled number of workers
   ↓
Inventory / Order services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The customer might see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"You are in the queue."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the system to process successful purchase attempts at a rate the downstream services can handle.&lt;/p&gt;




&lt;h1&gt;
  
  
  18. Checkout Is a Distributed Workflow
&lt;/h1&gt;

&lt;p&gt;Now let's put the major pieces together.&lt;/p&gt;

&lt;p&gt;Suppose the customer has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iPhone       ₹80,000
Headphones   ₹10,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and clicks:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The checkout workflow might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validate Cart
      ↓
Re-check Prices
      ↓
Reserve Inventory
      ↓
Calculate Final Amount
      ↓
Authorize Payment
      ↓
Create Order
      ↓
Confirm Inventory
      ↓
Publish Order Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that this involves multiple services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cart
Inventory
Pricing
Payment
Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each may have its own database.&lt;/p&gt;

&lt;p&gt;That creates a distributed transaction problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  19. Why Not Use One Giant Transaction?
&lt;/h1&gt;

&lt;p&gt;We might imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN

Reserve Inventory
Charge Payment
Create Order

COMMIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But inventory, payment, and orders may be separate services.&lt;/p&gt;

&lt;p&gt;Traditional database transactions don't naturally span all of them.&lt;/p&gt;

&lt;p&gt;Two-Phase Commit is one possible solution, but it introduces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocking
High latency
Tight coupling
Complex failure handling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a microservice architecture, a Saga is usually more practical.&lt;/p&gt;




&lt;h1&gt;
  
  
  20. The Saga Pattern
&lt;/h1&gt;

&lt;p&gt;A Saga breaks the checkout workflow into smaller operations.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Reserve Inventory
2. Authorize Payment
3. Create Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reserve Inventory → SUCCESS
Authorize Payment  → SUCCESS
Create Order       → FAILURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need compensating actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create Order → FAILED
       ↓
Void Payment Authorization
       ↓
Release Inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;A distributed transaction is replaced by a sequence of local transactions plus compensating actions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  21. Orchestrating the Checkout
&lt;/h1&gt;

&lt;p&gt;There are two common ways to implement a Saga.&lt;/p&gt;

&lt;p&gt;With choreography, services communicate through events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inventory Reserved
       ↓
Payment Service
       ↓
Payment Completed
       ↓
Order Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is loosely coupled, but large workflows can become difficult to understand.&lt;/p&gt;

&lt;p&gt;With orchestration, one service coordinates the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Order Orchestrator
                    |
       ┌────────────┼────────────┐
       ↓            ↓            ↓
  Inventory       Payment       Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The orchestrator knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What step comes next?
What happens when a step fails?
What compensation is required?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For checkout, orchestration is often easier to reason about and debug.&lt;/p&gt;




&lt;h1&gt;
  
  
  22. Payment Is Another Reliability Problem
&lt;/h1&gt;

&lt;p&gt;Now suppose the payment service calls an external payment provider.&lt;/p&gt;

&lt;p&gt;The request looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Our Payment Service
        ↓
Payment Gateway
        ↓
Bank / Card Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine the bank successfully charges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;₹10,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but our connection times out before we receive the response.&lt;/p&gt;

&lt;p&gt;Our service doesn't know whether the payment succeeded.&lt;/p&gt;

&lt;p&gt;If we simply retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retry
  ↓
Another ₹10,000 charge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the customer could be charged twice.&lt;/p&gt;

&lt;p&gt;This is why payment APIs must be idempotent.&lt;/p&gt;




&lt;h1&gt;
  
  
  23. Idempotency
&lt;/h1&gt;

&lt;p&gt;Every logical payment attempt gets an idempotency key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAYMENT-123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAYMENT-123
     ↓
SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A retry with the same key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAYMENT-123
     ↓
Already processed
     ↓
Return previous result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payment provider and our own payment service should both support this concept.&lt;/p&gt;

&lt;p&gt;The payment table can store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paymentId
orderId
amount
status
provider
idempotencyKey
createdAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and enforce uniqueness on the idempotency key.&lt;/p&gt;




&lt;h1&gt;
  
  
  24. Authorize First, Capture Later
&lt;/h1&gt;

&lt;p&gt;A useful payment flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order placement
      ↓
Payment authorization
      ↓
Order processing
      ↓
Product shipped
      ↓
Payment capture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authorization means the payment method has enough funds and the amount is reserved.&lt;/p&gt;

&lt;p&gt;If the order is cancelled before shipment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cancel order
      ↓
Void authorization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of charging the customer and then issuing a refund.&lt;/p&gt;

&lt;p&gt;This is particularly useful for physical goods because payment capture can be tied to fulfillment.&lt;/p&gt;




&lt;h1&gt;
  
  
  25. The Order State Machine
&lt;/h1&gt;

&lt;p&gt;An order should have explicit states.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PLACED
   ↓
PAYMENT_AUTHORIZED
   ↓
CONFIRMED
   ↓
PROCESSING
   ↓
SHIPPED
   ↓
DELIVERED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other possible states include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CANCELLED
RETURNED
REFUNDED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The state machine becomes very useful when failures happen.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment = AUTHORIZED
Order = PAYMENT_AUTHORIZED
Inventory = RESERVED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the order service crashes, we know where the workflow stopped.&lt;/p&gt;

&lt;p&gt;We can resume or compensate from that state.&lt;/p&gt;




&lt;h1&gt;
  
  
  26. Keeping Services in Sync With Kafka
&lt;/h1&gt;

&lt;p&gt;Once an order is successfully created, many other systems need to know about it.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Notification
Warehouse
Analytics
Recommendations
Seller dashboard
Fraud detection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't want checkout to wait for all of them.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create Order
     ↓
Publish OrderCreated
     ↓
Return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumers process the event asynchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    OrderCreated
                         |
          ┌──────────────┼──────────────┐
          ↓              ↓              ↓
    Notification      Warehouse      Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kafka is useful as the event backbone.&lt;/p&gt;

&lt;p&gt;Typical event streams might include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;product-events
inventory-events
order-events
user-events
click-events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps downstream systems decoupled from the checkout path.&lt;/p&gt;




&lt;h1&gt;
  
  
  27. What If Kafka Is Down?
&lt;/h1&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order saved successfully
        ↓
Kafka unavailable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We cannot afford to lose the &lt;code&gt;OrderCreated&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;A common solution is the transactional outbox pattern.&lt;/p&gt;

&lt;p&gt;Inside the same database transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN

Save Order
Save Outbox Event

COMMIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now both the order and the event are durable.&lt;/p&gt;

&lt;p&gt;A background worker later publishes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If Kafka is temporarily unavailable, the worker retries.&lt;/p&gt;

&lt;p&gt;This creates a reliable bridge between the transactional database and the asynchronous event system.&lt;/p&gt;




&lt;h1&gt;
  
  
  28. Making Kafka Processing Reliable
&lt;/h1&gt;

&lt;p&gt;Distributed messaging usually works with at-least-once delivery.&lt;/p&gt;

&lt;p&gt;That means a consumer may receive the same event more than once.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
    ↓
Consumer processes event
    ↓
Consumer crashes before acknowledging
    ↓
Kafka delivers event again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore consumers should be idempotent.&lt;/p&gt;

&lt;p&gt;A common pattern is to give each event a unique:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eventId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and record processed event IDs when necessary.&lt;/p&gt;

&lt;p&gt;We also need to monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kafka consumer lag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If consumers cannot keep up with producers, lag grows.&lt;/p&gt;

&lt;p&gt;Messages that repeatedly fail can be moved to a dead-letter queue for investigation.&lt;/p&gt;




&lt;h1&gt;
  
  
  29. API Design
&lt;/h1&gt;

&lt;p&gt;Once the core architecture is clear, the major APIs become straightforward.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/products/search?q=wireless+headphones&amp;amp;category=electronics&amp;amp;minPrice=5000&amp;amp;maxPrice=30000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/products/{productId}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to cart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/carts/{cartId}/items
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get cart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/carts/{cartId}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/orders
Idempotency-Key: checkout-123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/orders/{orderId}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/payments
Idempotency-Key: payment-123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important API design principle is that operations which can be retried should be made idempotent.&lt;/p&gt;




&lt;h1&gt;
  
  
  30. Data Model
&lt;/h1&gt;

&lt;p&gt;The catalog can use a document model because product attributes vary.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product
-------------------------
productId
sellerId
name
category
price
attributes
images
rating
createdAt
updatedAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inventory is more transactional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inventory
-------------------------
productId
warehouseId
availableQuantity
reservedQuantity
updatedAt

Primary Key:
(productId, warehouseId)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cart can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cart
-------------------------
cartId
userId
status
createdAt
updatedAt
expiresAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CartItem
-------------------------
cartId
productId
quantity
priceAtAddTime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Orders should preserve the actual price agreed during checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
-------------------------
orderId
userId
status
subtotal
totalAmount
currency
createdAt
updatedAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Order items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderItem
-------------------------
orderId
productId
sellerId
quantity
unitPrice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment
-------------------------
paymentId
orderId
amount
currency
status
provider
idempotencyKey
createdAt
updatedAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An order should never recalculate its historical price from the current product price.&lt;/p&gt;




&lt;h1&gt;
  
  
  31. Choosing the Databases
&lt;/h1&gt;

&lt;p&gt;There is no requirement to use one database for everything.&lt;/p&gt;

&lt;p&gt;A reasonable large-scale architecture might use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Catalog
   → MongoDB / DynamoDB

Search
   → Elasticsearch

Cart
   → Redis

Orders
   → PostgreSQL / MySQL

Inventory
   → Strongly consistent relational DB

Events
   → Kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Choose storage according to the access pattern and consistency requirement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a smaller system, PostgreSQL could handle much more of the platform.&lt;/p&gt;

&lt;p&gt;The more specialized architecture becomes useful as traffic and scale increase.&lt;/p&gt;




&lt;h1&gt;
  
  
  32. Partitioning and Sharding
&lt;/h1&gt;

&lt;p&gt;Eventually, an order database may become too large for one machine or one database cluster.&lt;/p&gt;

&lt;p&gt;Orders can be partitioned using a key such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if the common query is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Show all orders for this user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shard 1 → users A–F
Shard 2 → users G–M
Shard 3 → users N–S
Shard 4 → users T–Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact partitioning strategy depends on the workload.&lt;/p&gt;

&lt;p&gt;We must also watch for hot partitions.&lt;/p&gt;

&lt;p&gt;A very large seller or customer can generate disproportionately high traffic.&lt;/p&gt;




&lt;h1&gt;
  
  
  33. Multi-Warehouse Inventory
&lt;/h1&gt;

&lt;p&gt;Large marketplaces rarely have one warehouse.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bangalore → 5 iPhones
Mumbai    → 10 iPhones
Delhi     → 2 iPhones
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inventory therefore becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product
   |
   ├── Bangalore → 5
   ├── Mumbai    → 10
   └── Delhi     → 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a customer places an order, the system needs to decide where the order should be fulfilled.&lt;/p&gt;

&lt;p&gt;Possible factors include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Availability
Customer location
Shipping cost
Delivery time
Warehouse capacity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, we ship from a nearby warehouse that has inventory.&lt;/p&gt;




&lt;h1&gt;
  
  
  34. Multi-Seller Orders
&lt;/h1&gt;

&lt;p&gt;A marketplace can have products from several sellers in one cart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iPhone → Seller A
Shoes   → Seller B
Book    → Seller C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The customer experiences one checkout, but internally the platform may create separate fulfillment orders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer Order
      |
      ├── Seller A Order
      ├── Seller B Order
      └── Seller C Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each seller may ship independently.&lt;/p&gt;

&lt;p&gt;Payment may also need to be split into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer payment
      ↓
Platform commission
      ↓
Seller payouts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one reason marketplace systems are considerably more complicated than a simple online store.&lt;/p&gt;




&lt;h1&gt;
  
  
  35. Pricing and Promotions
&lt;/h1&gt;

&lt;p&gt;The final checkout price may involve several steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base price
   ↓
Discount
   ↓
Coupon
   ↓
Tax
   ↓
Shipping
   ↓
Final amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A promotion might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expiry
Usage limit
Eligible products
Eligible users
Minimum order value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pricing service must validate all of these.&lt;/p&gt;

&lt;p&gt;The client must never be trusted to supply the final price.&lt;/p&gt;




&lt;h1&gt;
  
  
  36. Recommendations and Reviews
&lt;/h1&gt;

&lt;p&gt;Recommendations don't need to block checkout.&lt;/p&gt;

&lt;p&gt;A recommendation system might use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Purchase history
Browsing history
Product similarity
Co-purchase patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customers who bought A
also bought B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recommendation results can be precomputed and stored in Redis.&lt;/p&gt;

&lt;p&gt;Reviews and ratings can also be handled asynchronously.&lt;/p&gt;

&lt;p&gt;A new review taking a few seconds to appear in an aggregate rating is generally acceptable.&lt;/p&gt;




&lt;h1&gt;
  
  
  37. Rate Limiting
&lt;/h1&gt;

&lt;p&gt;Rate limiting protects the system from abuse and sudden bursts.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search
→ relatively high limit

Add to Cart
→ lower limit

Checkout
→ much lower limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A distributed rate limiter can use Redis counters.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  ↓
API Gateway
  ↓
Rate Limiter
  ↓
Application Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During a flash sale, rate limiting becomes especially important because the goal is not merely to accept traffic but to protect downstream services from being overwhelmed.&lt;/p&gt;




&lt;h1&gt;
  
  
  38. CDN
&lt;/h1&gt;

&lt;p&gt;Product images and static assets are excellent CDN candidates.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
Application server
 ↓
Object storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
CDN
 ↓
Cache hit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a cache miss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CDN
 ↓
Origin
 ↓
Cache
 ↓
User
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps huge amounts of image and static-content traffic away from application servers.&lt;/p&gt;




&lt;h1&gt;
  
  
  39. Observability
&lt;/h1&gt;

&lt;p&gt;At this scale, knowing that a service is "up" isn't enough.&lt;/p&gt;

&lt;p&gt;We need to know whether the entire customer journey is healthy.&lt;/p&gt;

&lt;p&gt;Useful metrics include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search p95 / p99 latency
Product API latency
Checkout latency
Checkout success rate
Inventory reservation failures
Payment failure rate
Order creation failures
Redis hit ratio
Database latency
Database connection usage
Kafka consumer lag
Flash-sale queue depth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Distributed tracing can follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  ↓
API Gateway
  ↓
Order Service
  ↓
Inventory Service
  ↓
Payment Service
  ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A correlation ID makes it possible to trace one checkout across multiple services.&lt;/p&gt;




&lt;h1&gt;
  
  
  40. Failure Handling
&lt;/h1&gt;

&lt;p&gt;Distributed systems fail.&lt;/p&gt;

&lt;p&gt;The architecture should assume this rather than treating failure as an exceptional event.&lt;/p&gt;

&lt;p&gt;If the inventory service is unavailable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checkout
   ↓
Inventory unavailable
   ↓
Do not create a confirmed order
   ↓
Retry or fail gracefully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the payment gateway is unavailable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment unavailable
   ↓
Do not mark payment as successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Kafka is unavailable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Persist event in outbox
   ↓
Publish later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Redis is unavailable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the durable database where safe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, for flash-sale admission, Redis may be part of the high-throughput control path, so the system should have an explicit degraded mode rather than blindly bypassing it.&lt;/p&gt;




&lt;h1&gt;
  
  
  41. Inventory Reconciliation
&lt;/h1&gt;

&lt;p&gt;Because systems such as Redis, Kafka consumers, and databases can fail independently, inventory can temporarily drift.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis says:
10 available

Database says:
8 available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reconciliation process can periodically compare authoritative inventory with derived state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inventory DB
     ↓
Compare
     ↓
Redis
     ↓
Repair mismatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially important after flash sales.&lt;/p&gt;

&lt;p&gt;The database remains the durable source of truth.&lt;/p&gt;




&lt;h1&gt;
  
  
  42. CQRS and Read Models
&lt;/h1&gt;

&lt;p&gt;At very large scale, read traffic can be separated from write traffic.&lt;/p&gt;

&lt;p&gt;The write path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Command
   ↓
Transactional DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Events are then published:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transactional DB
       ↓
Kafka
       ↓
Read model builders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those consumers update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Elasticsearch
Redis
Other read models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Commands
                   |
                   ▼
              Write Model
                   |
                   ▼
              PostgreSQL
                   |
                 Kafka
                   |
          ┌────────┴────────┐
          ↓                 ↓
    Elasticsearch         Redis
      Read Model         Read Model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is essentially CQRS.&lt;/p&gt;

&lt;p&gt;The trade-off is eventual consistency between the write model and read models.&lt;/p&gt;

&lt;p&gt;That is acceptable for search and browsing, but not for the final inventory or payment decision.&lt;/p&gt;




&lt;h1&gt;
  
  
  43. Security
&lt;/h1&gt;

&lt;p&gt;E-commerce systems handle sensitive customer and payment information.&lt;/p&gt;

&lt;p&gt;Important controls include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication
Authorization
TLS
Rate limiting
Input validation
Audit logging
Fraud detection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Payment card information should normally be handled by a PCI-compliant payment provider rather than stored directly in the application database.&lt;/p&gt;

&lt;p&gt;The server must also validate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Price
Quantity
Seller
Product
Order ownership
Coupon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client should never be able to manipulate these values simply by changing the request.&lt;/p&gt;




&lt;h1&gt;
  
  
  44. Availability and Disaster Recovery
&lt;/h1&gt;

&lt;p&gt;A production platform should have multiple instances of critical services.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load Balancer
     |
 ┌───┼───┐
 ↓   ↓   ↓
S1  S2  S3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Databases should use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replication
Backups
Automated failover
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kafka should use replication.&lt;/p&gt;

&lt;p&gt;Redis should have appropriate replication and recovery depending on how it is being used.&lt;/p&gt;

&lt;p&gt;For disaster recovery, backups should be tested rather than merely configured.&lt;/p&gt;

&lt;p&gt;A recovery plan should define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RPO
How much data can we lose?

RTO
How quickly must we recover?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  45. Multi-Region Architecture
&lt;/h1&gt;

&lt;p&gt;As the platform becomes global, one region may no longer be enough.&lt;/p&gt;

&lt;p&gt;We might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;India
Europe
US
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with regional services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;India Region
   ↓
India Inventory
India Orders

Europe Region
   ↓
Europe Inventory
Europe Orders

US Region
   ↓
US Inventory
US Orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inventory is particularly difficult because it is mutable state.&lt;/p&gt;

&lt;p&gt;A product can exist in multiple warehouses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;India → 100
Europe → 50
US → 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should avoid making every inventory update depend on one globally shared database if possible.&lt;/p&gt;

&lt;p&gt;Instead, inventory can be partitioned by fulfillment region or warehouse.&lt;/p&gt;

&lt;p&gt;Catalog and search data can be replicated more freely because those workloads tolerate eventual consistency.&lt;/p&gt;




&lt;h1&gt;
  
  
  46. Incident: Elasticsearch Goes Down
&lt;/h1&gt;

&lt;p&gt;Suppose Elasticsearch becomes unhealthy.&lt;/p&gt;

&lt;p&gt;Search requests start failing or becoming slow.&lt;/p&gt;

&lt;p&gt;The important thing is that checkout should still work.&lt;/p&gt;

&lt;p&gt;A customer who already knows the product ID should still be able to open the product page through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product DB / Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route to healthy search replicas
       ↓
Reduce non-critical search features
       ↓
Recover Elasticsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search degradation should not bring down the entire commerce platform.&lt;/p&gt;




&lt;h1&gt;
  
  
  47. Incident: Inventory Reservation Leak
&lt;/h1&gt;

&lt;p&gt;Suppose customers complain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The product says sold out, but nobody can buy it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One possible cause is that reservations were never released.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer starts checkout
       ↓
Inventory reserved
       ↓
Payment fails
       ↓
Reservation never released
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available inventory → artificially decreases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reservation expiry mechanism and reconciliation job prevent this.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reservation created
       ↓
15-minute TTL
       ↓
Payment not completed
       ↓
Release inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  48. Incident: Duplicate Payment
&lt;/h1&gt;

&lt;p&gt;Suppose a customer sees two payment authorizations.&lt;/p&gt;

&lt;p&gt;We investigate using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orderId
paymentId
idempotencyKey
provider transaction ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identify duplicate
       ↓
Void/refund duplicate
       ↓
Fix retry/idempotency behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important lesson is that payment APIs should be designed for retries from the beginning.&lt;/p&gt;




&lt;h1&gt;
  
  
  49. Cost and Operational Trade-offs
&lt;/h1&gt;

&lt;p&gt;Every additional distributed system has an operational cost.&lt;/p&gt;

&lt;p&gt;Our architecture now contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis
Elasticsearch
Kafka
Multiple databases
Payment provider
CDN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Infrastructure cost
Monitoring
On-call responsibility
Failure modes
Operational complexity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a small e-commerce application, this would be overengineering.&lt;/p&gt;

&lt;p&gt;A much smaller system could start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monolith
   ↓
PostgreSQL
   ↓
Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and introduce Elasticsearch, Kafka, and separate services only when the scale justifies them.&lt;/p&gt;

&lt;p&gt;This is an important architectural principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't introduce distributed complexity until the workload requires it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  50. How the Architecture Evolves
&lt;/h1&gt;

&lt;p&gt;A sensible system does not need to start with twenty services.&lt;/p&gt;

&lt;p&gt;A small platform might begin as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monolith
   |
PostgreSQL
   |
Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The product catalog, cart, orders, and inventory can initially live in one application.&lt;/p&gt;

&lt;p&gt;As traffic grows, search can be separated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
    |
    ├── PostgreSQL
    ├── Redis
    └── Elasticsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As asynchronous workloads grow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
    |
    └── Kafka
          |
          ├── Notifications
          ├── Analytics
          └── Search indexing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As checkout becomes more complex:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;can be separated.&lt;/p&gt;

&lt;p&gt;During flash sales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis atomic counters
+
Queue
+
Dedicated workers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can protect the core transactional systems.&lt;/p&gt;

&lt;p&gt;Finally, global expansion can introduce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Multi-region services
Multi-warehouse inventory
Regional order processing
Disaster recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture evolves because the problems evolve.&lt;/p&gt;




&lt;h1&gt;
  
  
  51. The Most Important Trade-offs
&lt;/h1&gt;

&lt;p&gt;There is no universally correct choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL vs MongoDB
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is excellent for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
Payments
Inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because transactions matter.&lt;/p&gt;

&lt;p&gt;MongoDB/DynamoDB can be attractive for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flexible product attributes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Database Lock vs Atomic Update
&lt;/h2&gt;

&lt;p&gt;Row locks are easy to understand but can create contention.&lt;/p&gt;

&lt;p&gt;Atomic updates usually provide better throughput for simple inventory decrements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Redis vs Database
&lt;/h2&gt;

&lt;p&gt;Redis provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Speed
High throughput
Atomic counters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Durability
Strong source of truth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For flash sales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis → absorb the burst
Database → durable truth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Saga vs 2PC
&lt;/h2&gt;

&lt;p&gt;2PC gives stronger transactional coordination but introduces blocking and coupling.&lt;/p&gt;

&lt;p&gt;Saga uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local transactions
+
Compensation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and fits microservice workflows better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Synchronous vs Asynchronous
&lt;/h2&gt;

&lt;p&gt;Keep critical checkout operations synchronous.&lt;/p&gt;

&lt;p&gt;Move non-critical work to Kafka:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Email
Analytics
Recommendations
Seller notifications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Search Index vs Primary Database
&lt;/h2&gt;

&lt;p&gt;Elasticsearch is excellent for search but is not the source of truth.&lt;/p&gt;

&lt;p&gt;The primary catalog database remains authoritative.&lt;/p&gt;




&lt;h1&gt;
  
  
  52. The Final Architecture
&lt;/h1&gt;

&lt;p&gt;After all the deeper decisions, the architecture can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                           USERS
                             |
                             ▼
                       CDN / Gateway
                             |
            ┌────────────────┼─────────────────┐
            ↓                ↓                 ↓
         Product           Search              Cart
         Service           Service            Service
            |                |                 |
            ↓                ↓                 ↓
        Product DB     Elasticsearch          Redis
            |
            ↓
           Kafka
            |
      ┌─────┴──────────┐
      ↓                ↓
  Inventory          Other
   Service           Consumers
      |
      ↓
Strong Inventory DB
      |
      ↓
   Checkout
      |
 ┌────┼───────────────┐
 ↓    ↓               ↓
Inv  Pricing        Payment
                      |
                      ↓
               Payment Gateway
                      |
                      ↓
                    Order
                      |
                      ↓
                    Kafka
                      |
          ┌───────────┼───────────┐
          ↓           ↓           ↓
     Notification  Warehouse   Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During a flash sale, the inventory path changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Millions of Users
       ↓
Rate Limiter
       ↓
Queue
       ↓
Redis Atomic Counter
       ↓
Order Workers
       ↓
Inventory DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture therefore has two very different paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
CDN / Cache
 ↓
Search / Product DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optimized for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Very high throughput
Low latency
Eventual consistency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Transaction path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
Checkout
 ↓
Inventory
 ↓
Payment
 ↓
Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optimized for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correctness
Strong consistency
Idempotency
Failure recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  53. The Core Ideas to Remember
&lt;/h1&gt;

&lt;p&gt;The entire design becomes much easier to remember if we reduce it to a few principles.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;separate read-heavy workloads from write-critical workloads&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;Read-heavy
→ CDN
→ Redis
→ Elasticsearch

Write-critical
→ Transactional database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, &lt;strong&gt;inventory is the main concurrency problem&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;Many buyers
     ↓
Limited stock
     ↓
Exactly one valid reservation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third, &lt;strong&gt;checkout is a distributed transaction&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;Inventory
   +
Payment
   +
Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Saga
+
Compensation
+
Idempotency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fourth, &lt;strong&gt;flash sales require traffic absorption&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;Rate limiter
   ↓
Queue
   ↓
Redis
   ↓
Workers
   ↓
Durable DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fifth, &lt;strong&gt;Kafka moves non-critical work away from the request path&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;Order Created
      ↓
Kafka
      ↓
Notifications
Analytics
Warehouse
Recommendations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The database should remain the source of truth for business-critical state, while caches, search indexes, queues, and event streams help the system scale.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  54. How to Explain This in a System Design Interview
&lt;/h1&gt;

&lt;p&gt;A clean explanation can naturally progress like this:&lt;/p&gt;

&lt;p&gt;Start by saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'd separate the system into a read-heavy browsing path and a consistency-critical transaction path."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then explain the read path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Catalog
   ↓
Elasticsearch
   ↓
Redis / CDN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then move to checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cart
 ↓
Inventory
 ↓
Payment
 ↓
Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then explain the hardest problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The biggest correctness issue is preventing overselling when multiple users attempt to buy the last item concurrently."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Explain the normal solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Atomic database update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the flash-sale solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis atomic counter
+
Queue
+
Durable inventory database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then explain checkout consistency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Saga
+
Compensation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then payment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Idempotency
+
Authorization / Capture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then asynchronous processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, discuss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure recovery
Observability
Sharding
Multi-warehouse
Multi-region
Trade-offs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a natural conversation instead of a list of technologies.&lt;/p&gt;




&lt;h1&gt;
  
  
  55. Final Mental Model
&lt;/h1&gt;

&lt;p&gt;If you remember only one picture, remember this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         E-COMMERCE
                             |
             ┌───────────────┼────────────────┐
             ↓               ↓                ↓
          CATALOG          SEARCH             CART
             |               |                |
          Product DB     Elasticsearch       Redis
             |               |                |
             └───────────────┼────────────────┘
                             |
                          CHECKOUT
                             |
              ┌──────────────┼──────────────┐
              ↓              ↓              ↓
          INVENTORY        PRICING        PAYMENT
              |                             |
         Strong DB                    Payment Gateway
              |                             |
              └──────────────┬──────────────┘
                             ↓
                           ORDER
                             |
                             ↓
                           Kafka
                             |
              ┌──────────────┼──────────────┐
              ↓              ↓              ↓
        Notification     Warehouse       Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And during extreme traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Millions of users
       ↓
Rate Limiter
       ↓
Queue
       ↓
Redis
       ↓
Workers
       ↓
Transactional Systems
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key lesson is not that every e-commerce system must use MongoDB, Redis, Elasticsearch, Kafka, and microservices.&lt;/p&gt;

&lt;p&gt;The key lesson is &lt;strong&gt;why&lt;/strong&gt; each component exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Elasticsearch → scalable search
Redis         → fast reads / hot state / atomic counters
Kafka         → asynchronous event processing
Database      → durable business truth
Queue         → backpressure
Saga          → distributed workflow
Idempotency   → safe retries
CDN           → offload static traffic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once those relationships are clear, the architecture becomes much easier to design from scratch in an interview.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>scalability</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Designing an Online Auction / Bidding System</title>
      <dc:creator>Rohit Kori</dc:creator>
      <pubDate>Mon, 10 Aug 2026 16:36:17 +0000</pubDate>
      <link>https://dev.to/brawnybytes/designing-an-online-auction-bidding-system-1m2i</link>
      <guid>https://dev.to/brawnybytes/designing-an-online-auction-bidding-system-1m2i</guid>
      <description>&lt;p&gt;Imagine an auction website similar to eBay.&lt;/p&gt;

&lt;p&gt;A seller lists an item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vintage Camera
Starting bid: $500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other users can open the auction page and place bids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → $550
User B → $600
User C → $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everyone currently watching the auction should see the highest bid change in real time.&lt;/p&gt;

&lt;p&gt;Eventually, the auction ends. The highest valid bidder becomes the winner, receives a notification, and gets a limited amount of time to complete payment.&lt;/p&gt;

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

&lt;p&gt;But once we have millions of users, thousands of active auctions, real-time bid updates, concurrent bids, auction expiration, and payment failures, the design becomes an interesting distributed-systems problem.&lt;/p&gt;

&lt;p&gt;This article builds the system from the ground up and gradually addresses those problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Understanding the Auction
&lt;/h1&gt;

&lt;p&gt;Let's first define what an auction actually means in our system.&lt;/p&gt;

&lt;p&gt;A seller creates an auction for an item.&lt;/p&gt;

&lt;p&gt;Other users can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;View auction
   ↓
See current highest bid
   ↓
Place a higher bid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important rule is that an auction does not necessarily end at a fixed clock time.&lt;/p&gt;

&lt;p&gt;Instead, in this design:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The auction closes when there has been no higher bid for one hour.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:00 → User A bids $500
10:20 → User B bids $550
10:45 → User C bids $600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one-hour timer is effectively extended by the latest bid.&lt;/p&gt;

&lt;p&gt;If no higher bid arrives after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:45 + 1 hour
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the auction can be closed and User C becomes the winner.&lt;/p&gt;

&lt;p&gt;After that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Winner
   ↓
Payment notification
   ↓
10-minute payment window
   ↓
Payment succeeds → Auction succeeds
Payment fails/expires → Auction fails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  2. Requirements
&lt;/h1&gt;

&lt;p&gt;Before designing the system, let's make the rules explicit.&lt;/p&gt;

&lt;p&gt;A user should be able to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an auction
View an active auction
Place a bid
See the current highest bid in real time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system should:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Close an auction after one hour without a higher bid
Determine the winner
Notify the winner
Give the winner 10 minutes to pay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also a few important business rules.&lt;/p&gt;

&lt;p&gt;If two users submit the same bid amount, the first bid wins.&lt;/p&gt;

&lt;p&gt;A bidder can only have one active bid in a particular auction.&lt;/p&gt;

&lt;p&gt;However, the bidder can increase that bid later.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → $500
User A → $600
User A → $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latest higher bid becomes the user's current bid.&lt;/p&gt;

&lt;p&gt;At the same time, we still keep the complete bid history.&lt;/p&gt;

&lt;p&gt;So the system remembers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$500
$600
$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than keeping only &lt;code&gt;$700&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For simplicity, we won't require a separate user-provided TTL for auctions that never receive a bid.&lt;/p&gt;

&lt;p&gt;Search, payment processing, and inventory are treated as separate systems. Our focus is the auction service itself.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. What Does the System Need to Guarantee?
&lt;/h1&gt;

&lt;p&gt;The system has two very different kinds of data.&lt;/p&gt;

&lt;p&gt;The first is the live bidding experience.&lt;/p&gt;

&lt;p&gt;A customer watching an auction wants to see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current highest bid: $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the UI briefly shows &lt;code&gt;$650&lt;/code&gt; while another bid of &lt;code&gt;$700&lt;/code&gt; is being processed, that isn't necessarily catastrophic.&lt;/p&gt;

&lt;p&gt;The live-bidding path can therefore tolerate some eventual consistency.&lt;/p&gt;

&lt;p&gt;The second is winner selection.&lt;/p&gt;

&lt;p&gt;When the auction closes, we must be absolutely certain who won.&lt;/p&gt;

&lt;p&gt;We cannot have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client A → Winner: User A
Client B → Winner: User B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for the same auction.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Live bid display
    ↓
Eventual consistency acceptable

Winner determination
    ↓
Strong consistency required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is one of the most important ideas in the design.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Estimating the Scale
&lt;/h1&gt;

&lt;p&gt;Let's use the assumptions from the original design.&lt;/p&gt;

&lt;p&gt;Suppose the platform has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 billion daily active users
100,000 auctions created per day
10% of users place one bid per day
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100M bids/day
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The average bid rate is approximately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100M / 86,400
≈ 1,157 bids/sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But average traffic isn't enough.&lt;/p&gt;

&lt;p&gt;Traffic will be bursty.&lt;/p&gt;

&lt;p&gt;Some auctions will attract almost no attention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A → 3 bidders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while a popular auction might attract enormous traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction B → millions of viewers
Auction B → thousands of concurrent bids
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design therefore needs to handle both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Large overall traffic
+
Hot individual auctions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original design also assumes roughly a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:1 read-to-write ratio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means viewing auction state is much more common than placing bids.&lt;/p&gt;

&lt;p&gt;This becomes important when we design the real-time update path.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. The Basic Architecture
&lt;/h1&gt;

&lt;p&gt;At a high level, the system looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                           USERS
                             |
                             ▼
                       Load Balancer
                             |
             ┌───────────────┼────────────────┐
             ↓               ↓                ↓
       Auction Service   Bid Update       Fulfillment
             |             Service           Service
             |                |                |
             ↓                ↓                ↓
        Auction DB       Dispatcher       Auction DB
             |
             ↓
           Cache
             |
             ↓
            Kafka
             |
       ┌─────┴──────┐
       ↓            ↓
 Notifications   Reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several important components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction Service
    ↓
Creates auctions and accepts bids

Auction DB
    ↓
Durable source of truth

Cache
    ↓
Fast access to current auction state

Bid Update Service
    ↓
Maintains real-time connections to viewers

Dispatcher
    ↓
Routes bid updates to the correct Bid Update Service

Fulfillment Service
    ↓
Detects auctions that should end and processes winners

Notification Service
    ↓
Notifies winners

Reconciliation Service
    ↓
Detects and repairs abnormal states
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's build these pieces one by one.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Creating an Auction
&lt;/h1&gt;

&lt;p&gt;Creating an auction is relatively straightforward.&lt;/p&gt;

&lt;p&gt;The client sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/auctions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with information such as:&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;"itemId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"item-123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auction Service creates a row in the auction database.&lt;/p&gt;

&lt;p&gt;A simplified model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction
-------------------------
auction_id
owner_id
item_id
status
created_at
updated_at
expire_at
winner_id
winner_bid_id
winner_price
payment_expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important fields are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status
expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because they control the auction lifecycle.&lt;/p&gt;

&lt;p&gt;A newly created auction starts as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and its initial expiration time is established according to the auction's bidding rules.&lt;/p&gt;

&lt;p&gt;The service also places the auction state into the cache.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Why Make the Auction Service Stateless?
&lt;/h1&gt;

&lt;p&gt;The Auction Service does not need to remember auction state inside its own process.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction Service
      ↓
Cache / DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any Auction Service instance can process a request.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A
  ↓
Auction Service #1

User B
  ↓
Auction Service #7

User C
  ↓
Auction Service #12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of them can access the same external state.&lt;/p&gt;

&lt;p&gt;This is what makes the stateless design easy to scale horizontally.&lt;/p&gt;

&lt;p&gt;If one instance fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
Another Auction Service instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The auction data is still available.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. The Auction Database and Cache
&lt;/h1&gt;

&lt;p&gt;We need durable storage and fast access.&lt;/p&gt;

&lt;p&gt;The database contains the complete auction state and bid history.&lt;/p&gt;

&lt;p&gt;The cache contains the information we need frequently.&lt;/p&gt;

&lt;p&gt;A useful cache entry is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction:{auction_id}

{
    status,
    highest_bid,
    highest_bidder_id,
    updated_at,
    expire_at
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction:A123

status            = ACTIVE
highest_bid       = $700
highest_bidder_id = U456
updated_at        = 10:45:12
expire_at         = 11:45:12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache is useful because thousands or millions of users may repeatedly ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"What is the current highest bid?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't want every one of those reads to hit the database.&lt;/p&gt;

&lt;p&gt;But an important rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The cache is not the ultimate source of truth.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Auction DB contains the durable record.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. The First Consistency Problem
&lt;/h1&gt;

&lt;p&gt;Suppose we successfully write a bid to the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB write → SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but then the cache update fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache update → FAILURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database → $700
Cache    → $650
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a cache inconsistency.&lt;/p&gt;

&lt;p&gt;We can retry the cache update.&lt;/p&gt;

&lt;p&gt;But what if the retry also fails?&lt;/p&gt;

&lt;p&gt;We therefore need mechanisms to detect stale cache entries.&lt;/p&gt;

&lt;p&gt;That is why the cache stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;updated_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system can use that timestamp to determine whether cached information is sufficiently fresh.&lt;/p&gt;

&lt;p&gt;When necessary, it can read the database and repair the cache.&lt;/p&gt;

&lt;p&gt;This is essentially a form of read repair.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. How Do Users Receive Live Bid Updates?
&lt;/h1&gt;

&lt;p&gt;Now we reach one of the most interesting parts.&lt;/p&gt;

&lt;p&gt;Imagine 50,000 people are watching the same auction.&lt;/p&gt;

&lt;p&gt;When somebody bids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we need to push the update to all those viewers.&lt;/p&gt;

&lt;p&gt;We have several possible technologies.&lt;/p&gt;

&lt;p&gt;The main choices are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP polling
Long polling
WebSocket
Server-Sent Events (SSE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Polling would mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
 ↓
"Any new bid?"
 ↓
Server
 ↓
Client
 ↓
"Any new bid?"
 ↓
Server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates unnecessary traffic.&lt;/p&gt;

&lt;p&gt;Long polling is better, but still requires repeated HTTP requests.&lt;/p&gt;

&lt;p&gt;WebSocket provides a persistent bidirectional connection.&lt;/p&gt;

&lt;p&gt;SSE provides a persistent one-way connection:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For this auction system, the client mainly needs to receive updates.&lt;/p&gt;

&lt;p&gt;The bid itself can still be sent through a normal HTTP request.&lt;/p&gt;

&lt;p&gt;Therefore SSE is a natural fit.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Why SSE Works Well Here
&lt;/h1&gt;

&lt;p&gt;The two directions are different.&lt;/p&gt;

&lt;p&gt;When a user places a bid:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;we can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/auctions/{auctionId}/bids
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the server tells users that somebody else has placed a higher bid:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;we can use SSE.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid placement
    ↓
HTTP

Live updates
    ↓
SSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WebSocket would also work, especially if the product later requires richer bidirectional real-time communication.&lt;/p&gt;

&lt;p&gt;But for simple one-way live updates, SSE is less complex.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Connecting a User to a Bid Update Service
&lt;/h1&gt;

&lt;p&gt;When a user opens an active auction page, the client first gets the auction details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/auctions/{auctionId}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the auction is still active, the browser opens an SSE connection.&lt;/p&gt;

&lt;p&gt;A load balancer may route the connection to any Bid Update Service instance.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User U1 → bus1
User U2 → bus1
User U3 → bus2
User U4 → bus3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Bid Update Service keeps an in-memory subscription table.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bus1

auction A1 → [U1, U2]
auction A2 → [U5]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the service:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These users are currently watching these auctions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  13. Why Do We Need a Dispatcher?
&lt;/h1&gt;

&lt;p&gt;Suppose a bid arrives for auction &lt;code&gt;A1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Auction Service knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction = A1
New highest bid = $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But which Bid Update Service has the viewers?&lt;/p&gt;

&lt;p&gt;It might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bus1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auction Service should not need to know the internal connection state of every Bid Update Service.&lt;/p&gt;

&lt;p&gt;So we introduce a Dispatcher.&lt;/p&gt;

&lt;p&gt;The Dispatcher maintains another subscription table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dispatcher

A1 → bus1
A2 → bus2
A3 → bus1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User places bid
       ↓
Auction Service
       ↓
Dispatcher
       ↓
Correct Bid Update Service
       ↓
Connected viewers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction Service
    → process business logic

Dispatcher
    → route update

Bid Update Service
    → maintain client connections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  14. The Full Bid Update Flow
&lt;/h1&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A1
Current bid = $600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User U10 places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request goes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The Auction Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checks auction status
       ↓
Writes bid to DB
       ↓
Updates highest bid in cache
       ↓
Sends update to Dispatcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dispatcher checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A1 → bus1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and forwards the update:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The Bid Update Service checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A1 → [U1, U2, U3, U4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New highest bid = $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to those SSE connections.&lt;/p&gt;

&lt;p&gt;The complete flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bidder
  ↓
Auction Service
  ↓
Auction DB + Cache
  ↓
Dispatcher
  ↓
Bid Update Service
  ↓
SSE
  ↓
All viewers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  15. Why Not Just Poll the Database?
&lt;/h1&gt;

&lt;p&gt;A naive design would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid Update Service
       ↓
Poll Auction DB
       ↓
Find new bids
       ↓
Push to users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;But imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100,000 active auctions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and each auction is being polled every few seconds.&lt;/p&gt;

&lt;p&gt;The database would receive enormous numbers of unnecessary queries.&lt;/p&gt;

&lt;p&gt;Most queries would return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nothing changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of constantly asking the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Did something happen?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we push the event when something actually happens.&lt;/p&gt;

&lt;p&gt;That is much more efficient.&lt;/p&gt;




&lt;h1&gt;
  
  
  16. Making the Dispatcher Highly Available
&lt;/h1&gt;

&lt;p&gt;The Dispatcher is stateful because it maintains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction → Bid Update Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Dispatcher fails, bid updates cannot be routed to viewers.&lt;/p&gt;

&lt;p&gt;The actual auction may still work, but the live experience breaks.&lt;/p&gt;

&lt;p&gt;There are several ways to make it resilient.&lt;/p&gt;

&lt;p&gt;One option is to maintain a write-ahead log and snapshots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subscription changes
        ↓
WAL
        ↓
Snapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Dispatcher crashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Snapshot
   +
WAL
   ↓
Rebuild subscription table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is to replicate the state into an external key-value store.&lt;/p&gt;

&lt;p&gt;A third option is an active-standby design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Primary Dispatcher
       ↓
Standby Dispatcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the primary fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Standby → becomes primary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  17. Could We Remove the Dispatcher?
&lt;/h1&gt;

&lt;p&gt;Yes.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction Service
      ↓
Dispatcher
      ↓
Bid Update Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we could maintain the subscription mapping in a distributed key-value or coordination service.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid Update Service
      ↓
Coordination Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the Auction Service can look up which Bid Update Service is responsible for an auction.&lt;/p&gt;

&lt;p&gt;There is a trade-off.&lt;/p&gt;

&lt;p&gt;With a Dispatcher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pros:
- Auction Service has less responsibility
- Dispatcher can scale independently
- Retry logic is centralized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cons:
- Additional component
- More operational complexity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without a Dispatcher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pros:
- Simpler architecture
- Fewer components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cons:
- Auction Service handles forwarding
- Retry logic becomes its responsibility
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right choice depends on how much complexity the system can justify.&lt;/p&gt;




&lt;h1&gt;
  
  
  18. What Happens If a Bid Update Is Lost?
&lt;/h1&gt;

&lt;p&gt;Real-time systems sometimes lose messages.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700 bid happens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but one client never receives the update.&lt;/p&gt;

&lt;p&gt;Is the auction broken?&lt;/p&gt;

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

&lt;p&gt;During an active auction, another bid may soon arrive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
 ↓
$750
 ↓
$800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The missing &lt;code&gt;$700&lt;/code&gt; event becomes less important because newer updates overwrite the displayed state.&lt;/p&gt;

&lt;p&gt;The dangerous case is the &lt;strong&gt;last bid&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is the final bid, and the client never receives it.&lt;/p&gt;

&lt;p&gt;A useful recovery mechanism is to have the client periodically check for stale updates.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No bid update for a while
        ↓
Hard pull
        ↓
GET /auctions/{id}
        ↓
Retrieve current authoritative state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fast push
+
Occasional authoritative pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and makes the system resilient to lost live events.&lt;/p&gt;




&lt;h1&gt;
  
  
  19. Placing a Bid
&lt;/h1&gt;

&lt;p&gt;Now let's look more closely at the actual bid request.&lt;/p&gt;

&lt;p&gt;The client sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/auctions/{auctionId}/bids
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&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;"bidAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"req-123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auction Service first checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Does auction exist?
Is status ACTIVE?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can check the cache first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
 ↓
ACTIVE?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the cache doesn't contain the auction, it can fall back to the database.&lt;/p&gt;

&lt;p&gt;This cache miss should normally be a corner case.&lt;/p&gt;




&lt;h1&gt;
  
  
  20. Recording Bid History
&lt;/h1&gt;

&lt;p&gt;Once the auction is confirmed to be active, the bid is written to the bid table.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid
-------------------------
bid_id
auction_id
bidder_id
amount
request_id
created_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original design uses an append-only pattern.&lt;/p&gt;

&lt;p&gt;That means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A → $500
User A → $600
User A → $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;creates three records.&lt;/p&gt;

&lt;p&gt;We don't overwrite the previous rows.&lt;/p&gt;

&lt;p&gt;This gives us a complete audit trail.&lt;/p&gt;

&lt;p&gt;The latest valid bid for a bidder can be treated as their current bid.&lt;/p&gt;

&lt;p&gt;The request ID or insertion timestamp can help determine ordering more robustly than relying only on client timestamps.&lt;/p&gt;




&lt;h1&gt;
  
  
  21. Updating the Highest Bid
&lt;/h1&gt;

&lt;p&gt;After the bid is persisted, the service checks whether it is higher than the current cached bid.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache:
highest_bid = $600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the new bid is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;highest_bid
    ↓
$700

highest_bidder_id
    ↓
U10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache is updated.&lt;/p&gt;

&lt;p&gt;Then the Dispatcher is notified so that live viewers receive the new value.&lt;/p&gt;

&lt;p&gt;If the new bid is lower than the current highest bid, it is still stored in the bid history but does not change the current highest-bid state.&lt;/p&gt;

&lt;p&gt;Under our business rule, a bidder can only increase their own bid.&lt;/p&gt;




&lt;h1&gt;
  
  
  22. Why the Append-Only Bid Table Is Useful
&lt;/h1&gt;

&lt;p&gt;An append-only design gives us several advantages.&lt;/p&gt;

&lt;p&gt;It provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Complete history
Auditability
High write throughput
Simple writes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid 101 → U1 → $500
Bid 102 → U2 → $550
Bid 103 → U1 → $600
Bid 104 → U3 → $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can later reconstruct what happened.&lt;/p&gt;

&lt;p&gt;It also avoids repeatedly modifying one large bid record.&lt;/p&gt;




&lt;h1&gt;
  
  
  23. A Hot Auction Creates a Hot Key
&lt;/h1&gt;

&lt;p&gt;There is an important scaling problem.&lt;/p&gt;

&lt;p&gt;Most auctions may have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5–10 bidders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but one extremely popular auction could have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Millions of viewers
Thousands of concurrent bids
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of these operations revolve around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id = A123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If our cache partitions by auction ID, all updates may land on the same partition.&lt;/p&gt;

&lt;p&gt;This is a classic hot-key problem.&lt;/p&gt;

&lt;p&gt;A single auction can become a bottleneck even when the overall system has plenty of capacity.&lt;/p&gt;




&lt;h1&gt;
  
  
  24. Handling Hot Auctions
&lt;/h1&gt;

&lt;p&gt;There are several approaches.&lt;/p&gt;

&lt;p&gt;One option is to use a lease or lock mechanism to coordinate concurrent updates.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid request
    ↓
Acquire lease for auction A123
    ↓
Update highest bid
    ↓
Release lease
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage is that concurrent writers are coordinated.&lt;/p&gt;

&lt;p&gt;The disadvantage is that a request may have to retry if another writer currently owns the lease.&lt;/p&gt;

&lt;p&gt;Another approach is replicated storage with quorum-style behavior.&lt;/p&gt;

&lt;p&gt;If the system is designed so that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Higher bid always wins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then conflict resolution becomes relatively simple.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replica A → $700
Replica B → $750
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The conflict resolver can choose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;max($700, $750)
= $750
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works particularly well because the business rule does not allow a bidder to reduce their own bid.&lt;/p&gt;




&lt;h1&gt;
  
  
  25. Auction Expiration Is a Scheduling Problem
&lt;/h1&gt;

&lt;p&gt;Eventually, the auction must end.&lt;/p&gt;

&lt;p&gt;We don't want every Auction Service instance constantly scanning every auction.&lt;/p&gt;

&lt;p&gt;Instead, we can use a Fulfillment Service.&lt;/p&gt;

&lt;p&gt;Its job is similar to a scheduler.&lt;/p&gt;

&lt;p&gt;It periodically looks for auctions whose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expire_at &amp;lt;= now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache can make this check efficient because it already contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status
expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  26. Determining the Winner
&lt;/h1&gt;

&lt;p&gt;The Fulfillment Service finds an auction that appears ready to close.&lt;/p&gt;

&lt;p&gt;But we should not blindly trust the cache.&lt;/p&gt;

&lt;p&gt;The cache might be stale.&lt;/p&gt;

&lt;p&gt;So the service asks the Auction DB to verify the winner.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fulfillment Service
        ↓
"Is this really the current winning bid?"
        ↓
Auction DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the cache was stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache → $700
DB    → $750
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the Fulfillment Service can repair the cache.&lt;/p&gt;

&lt;p&gt;This is another example of read repair.&lt;/p&gt;




&lt;h1&gt;
  
  
  27. Moving the Auction to Payment
&lt;/h1&gt;

&lt;p&gt;Once the winner is confirmed, the auction transitions from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;winner_id
winner_bid_id
winner_price
payment_expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values should be updated together as one logical state transition.&lt;/p&gt;

&lt;p&gt;The cache is updated as well.&lt;/p&gt;

&lt;p&gt;Then the notification system sends a message to the winner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Congratulations!

You won the auction for $750.

Please complete payment within 10 minutes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The live viewers can also receive an auction-closed update through the Dispatcher.&lt;/p&gt;




&lt;h1&gt;
  
  
  28. Payment Completion
&lt;/h1&gt;

&lt;p&gt;Payment itself is handled by a separate payment system.&lt;/p&gt;

&lt;p&gt;The auction does not need to own the payment implementation.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction
   ↓
PAYMENT_PENDING
   ↓
Payment Service
   ↓
Payment succeeds
   ↓
Auction → SUCCEEDED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payment service should be idempotent so retries don't accidentally create duplicate charges.&lt;/p&gt;

&lt;p&gt;The auction system only needs to reliably react to the final payment result.&lt;/p&gt;




&lt;h1&gt;
  
  
  29. What If the Winner Doesn't Pay?
&lt;/h1&gt;

&lt;p&gt;The winner has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to complete payment.&lt;/p&gt;

&lt;p&gt;The Fulfillment Service periodically checks auctions in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payment_expire_at &amp;lt; now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the payment hasn't succeeded:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The item can then be handled according to the broader marketplace policy.&lt;/p&gt;

&lt;p&gt;The important point is that the auction system has another timed state transition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTIVE
   ↓
PAYMENT_PENDING
   ↓
SUCCEEDED / FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  30. Why the Fulfillment Service Reads the Cache
&lt;/h1&gt;

&lt;p&gt;There is a deliberate trade-off here.&lt;/p&gt;

&lt;p&gt;The Fulfillment Service could query the database directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find every ACTIVE auction
   ↓
Check expire_at
   ↓
Find highest bid
   ↓
Execute expired auctions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that this can require expensive queries over the Auction DB.&lt;/p&gt;

&lt;p&gt;Instead, the cache already contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status
highest_bid
highest_bidder_id
expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the Fulfillment Service can use the cache to find candidates quickly.&lt;/p&gt;

&lt;p&gt;The trade-off is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache approach
    ↓
Lower latency
Less DB load
But possible stale data

Database approach
    ↓
More accurate
No cache dependency
But more DB load and more expensive queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final design can combine both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache → find candidate
   ↓
DB → verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us both performance and correctness.&lt;/p&gt;




&lt;h1&gt;
  
  
  31. The Reconciliation Service
&lt;/h1&gt;

&lt;p&gt;Distributed systems can end up in abnormal states.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment succeeded
       ↓
Auction DB was not updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment = SUCCESS
Auction  = PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Reconciliation Service periodically searches for these inconsistencies.&lt;/p&gt;

&lt;p&gt;It can compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction state
Payment state
Cache state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and repair the auction.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment says SUCCESS
Auction says PAYMENT_PENDING
       ↓
Reconciliation
       ↓
Auction → SUCCEEDED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the system a recovery mechanism instead of relying only on the happy path.&lt;/p&gt;




&lt;h1&gt;
  
  
  32. The Complete Stateless Design
&lt;/h1&gt;

&lt;p&gt;Putting everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                           USER
                            |
                            ▼
                       Load Balancer
                            |
                 ┌──────────┴──────────┐
                 ↓                     ↓
          Auction Service       Bid Update Service
                 |                     |
          ┌──────┴──────┐              |
          ↓             ↓              |
      Auction DB      Cache            |
          |             |              |
          └──────┬──────┘              |
                 ↓                     |
               Kafka                   |
                 |                     |
                 ↓                     |
          Async Consumers              |
                                       |
User SSE ←─────────────────────────────┘

Auction Service
      |
      ↓
Dispatcher
      |
      ↓
Bid Update Service
      |
      ↓
SSE → viewers

Cache
      |
      ↓
Fulfillment Service
      |
      ↓
Auction DB
      |
      ↓
Winner
      |
      ↓
Notification
      |
      ↓
Payment Service
      |
      ↓
SUCCEEDED / FAILED

Reconciliation Service
      |
      └── checks abnormal states
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auction Service itself remains stateless.&lt;/p&gt;

&lt;p&gt;The state lives in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction DB
Cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and temporary connection state lives in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid Update Service
Dispatcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  33. Stateful Auction Service
&lt;/h1&gt;

&lt;p&gt;So far we've used a stateless architecture.&lt;/p&gt;

&lt;p&gt;There is another interesting option.&lt;/p&gt;

&lt;p&gt;We could make the Auction Service itself stateful.&lt;/p&gt;

&lt;p&gt;When an auction is created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123
      ↓
Assigned to Auction Service #5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All bids for that auction are then routed to the same server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123
      ↓
Auction Service #5
      ↓
All bids
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server could keep the current auction state in memory.&lt;/p&gt;

&lt;p&gt;This reduces the need for multiple servers to coordinate on the same auction.&lt;/p&gt;




&lt;h1&gt;
  
  
  34. Routing Requests to the Correct Server
&lt;/h1&gt;

&lt;p&gt;With a stateful design, the load balancer needs to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123 → Server #5
Auction A456 → Server #8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requires service discovery or a consistent routing mechanism.&lt;/p&gt;

&lt;p&gt;The request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /auctions/A123/bids
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;must always reach the instance responsible for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can make per-auction ordering and consistency easier.&lt;/p&gt;




&lt;h1&gt;
  
  
  35. The Problem With Stateful Servers
&lt;/h1&gt;

&lt;p&gt;The stateful design introduces a new problem.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123
      ↓
Server #5
      ↓
In-memory state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and Server #5 crashes.&lt;/p&gt;

&lt;p&gt;We lose the in-memory state.&lt;/p&gt;

&lt;p&gt;Therefore, the server needs recovery mechanisms such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write-ahead log
+
Snapshots
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or rebuilding state from the Auction DB.&lt;/p&gt;

&lt;p&gt;We may also replicate the state:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;so that the follower can take over after failure.&lt;/p&gt;

&lt;p&gt;This makes the architecture more complicated.&lt;/p&gt;




&lt;h1&gt;
  
  
  36. Stateless vs Stateful
&lt;/h1&gt;

&lt;p&gt;The two approaches have different strengths.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Stateless&lt;/th&gt;
&lt;th&gt;Stateful&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;td&gt;More coordination required&lt;/td&gt;
&lt;td&gt;Easier per-auction ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;td&gt;Easier&lt;/td&gt;
&lt;td&gt;Harder because state must be recovered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Easier to add nodes&lt;/td&gt;
&lt;td&gt;More difficult due to routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot auctions&lt;/td&gt;
&lt;td&gt;Can be challenging&lt;/td&gt;
&lt;td&gt;One server can become a hotspot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;td&gt;External state survives node loss&lt;/td&gt;
&lt;td&gt;In-memory state needs recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational complexity&lt;/td&gt;
&lt;td&gt;Generally simpler&lt;/td&gt;
&lt;td&gt;More complex&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The stateless approach is usually more common.&lt;/p&gt;

&lt;p&gt;The stateful approach is still useful when processing a stream of events belonging to the same entity.&lt;/p&gt;




&lt;h1&gt;
  
  
  37. High Availability
&lt;/h1&gt;

&lt;p&gt;Let's examine what happens when individual components fail.&lt;/p&gt;

&lt;p&gt;The stateless Auction Service is relatively easy to make highly available.&lt;/p&gt;

&lt;p&gt;If one node fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Retry
   ↓
Another Auction Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the state is external, the new node can continue processing.&lt;/p&gt;

&lt;p&gt;Duplicate requests are possible.&lt;/p&gt;

&lt;p&gt;For auction creation, we can use an idempotent request or an upsert-like operation to prevent duplicate auctions.&lt;/p&gt;

&lt;p&gt;For bids, the append-only model makes duplicate writes easier to handle, especially when requests have unique IDs.&lt;/p&gt;




&lt;h1&gt;
  
  
  38. Dispatcher Availability
&lt;/h1&gt;

&lt;p&gt;The Dispatcher is different because it maintains state.&lt;/p&gt;

&lt;p&gt;If it fails, the routing table disappears.&lt;/p&gt;

&lt;p&gt;Possible solutions include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WAL + snapshots
External replicated KV store
Active / standby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Stateful components need a recovery story.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  39. Bid Update Service Availability
&lt;/h1&gt;

&lt;p&gt;The Bid Update Service maintains live client connections.&lt;/p&gt;

&lt;p&gt;Its state is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User connection
Auction subscription
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this state is tied to the lifetime of the connection.&lt;/p&gt;

&lt;p&gt;If the server crashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SSE connection dies
       ↓
Client reconnects
       ↓
Another Bid Update Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't necessarily need to persist every connection in a durable database.&lt;/p&gt;

&lt;p&gt;The connection state is temporary.&lt;/p&gt;

&lt;p&gt;This is different from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction state
Bid history
Winner
Payment state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which must survive server failures.&lt;/p&gt;




&lt;h1&gt;
  
  
  40. Cache and Database Replication
&lt;/h1&gt;

&lt;p&gt;The cache and Auction DB are both important infrastructure.&lt;/p&gt;

&lt;p&gt;Different replication strategies are possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Single leader
Multi-leader
Quorum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single leader is simpler.&lt;/p&gt;

&lt;p&gt;A replicated cache/database can improve availability.&lt;/p&gt;

&lt;p&gt;Quorum replication can improve durability and consistency at the cost of more coordination.&lt;/p&gt;

&lt;p&gt;The right strategy depends on the underlying technology and the consistency guarantees we need.&lt;/p&gt;

&lt;p&gt;For the auction's authoritative state, correctness during winner selection is more important than squeezing out the last bit of write latency.&lt;/p&gt;




&lt;h1&gt;
  
  
  41. Scaling the Auction Service
&lt;/h1&gt;

&lt;p&gt;In the stateless architecture, scaling is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;More traffic
    ↓
Add more Auction Service instances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A load balancer distributes requests.&lt;/p&gt;

&lt;p&gt;In a stateful design, scaling is harder because auctions need to be assigned to specific servers.&lt;/p&gt;

&lt;p&gt;We can shard auctions using a key such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;owner_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original design notes that &lt;code&gt;auction_id&lt;/code&gt; gives good co-location, but can create hot partitions.&lt;/p&gt;

&lt;p&gt;Partitioning by &lt;code&gt;owner_id&lt;/code&gt; may distribute traffic differently.&lt;/p&gt;

&lt;p&gt;The correct partition key depends on the actual workload.&lt;/p&gt;




&lt;h1&gt;
  
  
  42. Scaling the Dispatcher
&lt;/h1&gt;

&lt;p&gt;The Dispatcher keeps a table such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction → Bid Update Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The memory footprint can be manageable.&lt;/p&gt;

&lt;p&gt;But memory size isn't the only concern.&lt;/p&gt;

&lt;p&gt;The Dispatcher may receive a very high number of requests.&lt;/p&gt;

&lt;p&gt;Therefore, we can scale it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read replicas
Sharding
Partitioning by auction_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replication can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Synchronous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for stronger consistency, or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Asynchronous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when eventual consistency is acceptable.&lt;/p&gt;




&lt;h1&gt;
  
  
  43. Scaling the Cache and Auction Database
&lt;/h1&gt;

&lt;p&gt;There are several possible partitioning strategies.&lt;/p&gt;

&lt;p&gt;Partition by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has a useful property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction data
+
Bid data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be co-located.&lt;/p&gt;

&lt;p&gt;But a popular auction can become a hot partition.&lt;/p&gt;

&lt;p&gt;Another option is partitioning by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can distribute writes more evenly because an individual user is less likely to become a massive hotspot.&lt;/p&gt;

&lt;p&gt;Rate limiting can further protect the system from unusually active users.&lt;/p&gt;

&lt;p&gt;There is no universally correct partition key.&lt;/p&gt;

&lt;p&gt;We choose based on the workload.&lt;/p&gt;




&lt;h1&gt;
  
  
  44. Scaling Bid Update Services
&lt;/h1&gt;

&lt;p&gt;Bid Update Services are relatively easy to scale.&lt;/p&gt;

&lt;p&gt;Each node maintains its own in-memory connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bus1 → users
bus2 → users
bus3 → users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the number of connections grows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add more Bid Update Service instances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The load balancer distributes new SSE connections across them.&lt;/p&gt;

&lt;p&gt;The Dispatcher keeps track of which service owns the subscriptions.&lt;/p&gt;




&lt;h1&gt;
  
  
  45. Scaling Fulfillment
&lt;/h1&gt;

&lt;p&gt;The Fulfillment Service can also be distributed.&lt;/p&gt;

&lt;p&gt;Auctions can be partitioned by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and different workers can process different partitions.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Worker 1 → A–F
Worker 2 → G–M
Worker 3 → N–S
Worker 4 → T–Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important requirement is to prevent two workers from closing the same auction simultaneously.&lt;/p&gt;

&lt;p&gt;The final winner transition must therefore be protected by an atomic database update or equivalent concurrency control.&lt;/p&gt;




&lt;h1&gt;
  
  
  46. Cache and Auction DB Consistency
&lt;/h1&gt;

&lt;p&gt;Let's revisit one of the most subtle problems.&lt;/p&gt;

&lt;p&gt;Suppose the system does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Write bid to DB
2. Update cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DB write succeeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB = $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the cache update fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache = $650
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A retry can fix it.&lt;/p&gt;

&lt;p&gt;But retries can also fail.&lt;/p&gt;

&lt;p&gt;Therefore, the cache entry includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;updated_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the system detects that the cached state is stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
  ↓
Stale?
  ↓
Read DB
  ↓
Repair cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can happen when:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serving a read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Executing an auction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the cache is treated as a fast representation of the state rather than the final authority.&lt;/p&gt;




&lt;h1&gt;
  
  
  47. Write-Through vs Write-Back
&lt;/h1&gt;

&lt;p&gt;There are two broad ways to synchronize cache and database.&lt;/p&gt;

&lt;p&gt;With write-through-style behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write DB
   ↓
Update Cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database is updated immediately.&lt;/p&gt;

&lt;p&gt;With write-back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Update Cache
   ↓
Persist to DB later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write-back can reduce database writes in some workloads.&lt;/p&gt;

&lt;p&gt;For example, if we wanted to update the winning bid in the auction table on every bid, write-back could reduce the number of direct database writes.&lt;/p&gt;

&lt;p&gt;But it also makes durability and failure recovery more complicated.&lt;/p&gt;

&lt;p&gt;For the auction design, keeping the bid history durably in the database and using the cache for the current highest-bid state is a safer model.&lt;/p&gt;




&lt;h1&gt;
  
  
  48. SSE vs WebSocket
&lt;/h1&gt;

&lt;p&gt;Both technologies can provide real-time communication.&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;SSE&lt;/th&gt;
&lt;th&gt;WebSocket&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direction&lt;/td&gt;
&lt;td&gt;Server → Client&lt;/td&gt;
&lt;td&gt;Bidirectional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol style&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;WebSocket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;Text/event stream&lt;/td&gt;
&lt;td&gt;Text + binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconnection&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;Application typically handles it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;One-way live updates&lt;/td&gt;
&lt;td&gt;Interactive two-way communication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For our auction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid placement
→ HTTP

Bid updates
→ SSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simple because the server mainly pushes state to viewers.&lt;/p&gt;

&lt;p&gt;WebSocket becomes more attractive if the product eventually needs richer bidirectional interaction.&lt;/p&gt;




&lt;h1&gt;
  
  
  49. Another Real-Time Design
&lt;/h1&gt;

&lt;p&gt;There is another possible connection strategy.&lt;/p&gt;

&lt;p&gt;Instead of opening an SSE connection every time a user navigates to an auction, the application could maintain one long-lived WebSocket connection after login.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User logs in
     ↓
WebSocket established
     ↓
User opens Auction A
     ↓
Subscribe to A
     ↓
User opens Auction B
     ↓
Unsubscribe A
     ↓
Subscribe B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may be useful if users frequently move between auctions.&lt;/p&gt;

&lt;p&gt;The right choice depends on how users interact with the product.&lt;/p&gt;




&lt;h1&gt;
  
  
  50. Reliability of Live Updates
&lt;/h1&gt;

&lt;p&gt;We don't necessarily need exactly-once delivery for every live bid update.&lt;/p&gt;

&lt;p&gt;Suppose a client receives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;twice.&lt;/p&gt;

&lt;p&gt;The UI can simply keep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;max(currentBid, receivedBid)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, if the client receives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700
$750
$800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;$750&lt;/code&gt; is duplicated, there is no business impact.&lt;/p&gt;

&lt;p&gt;This makes the real-time layer easier to design.&lt;/p&gt;

&lt;p&gt;The important part is that the final authoritative winner comes from the database.&lt;/p&gt;




&lt;h1&gt;
  
  
  51. API Design
&lt;/h1&gt;

&lt;p&gt;The core API surface can be kept simple.&lt;/p&gt;

&lt;p&gt;Create an auction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/auctions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get an auction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/auctions/{auctionId}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place a bid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/auctions/{auctionId}/bids
Idempotency-Key: bid-123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get bid history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/auctions/{auctionId}/bids
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open live updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/v1/auctions/{auctionId}/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;implemented as an SSE stream.&lt;/p&gt;

&lt;p&gt;The API should return conflicts such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction not found
Auction already closed
Bid is not higher than current bid
Duplicate request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with appropriate HTTP status codes.&lt;/p&gt;




&lt;h1&gt;
  
  
  52. Data Model
&lt;/h1&gt;

&lt;p&gt;A simplified auction table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction
--------------------------------
auction_id
owner_id
item_id
status
created_at
updated_at
expire_at
winner_id
winner_bid_id
winner_price
payment_expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible statuses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTIVE
PAYMENT_PENDING
SUCCEEDED
FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bid table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid
--------------------------------
bid_id
auction_id
bidder_id
amount
request_id
created_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indexes should support common access patterns such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id + created_at
auction_id + amount
auction_id + bidder_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auction_id
status
highest_bid
highest_bidder_id
updated_at
expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid table
→ complete history

Cache
→ current hot state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  53. Auction State Transitions
&lt;/h1&gt;

&lt;p&gt;The lifecycle can be visualized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌──────────────┐
                 │    ACTIVE    │
                 └──────┬───────┘
                        │
                 no higher bid
                    for 1 hour
                        │
                        ▼
              ┌───────────────────┐
              │ PAYMENT_PENDING   │
              └─────────┬─────────┘
                        │
                 ┌──────┴───────┐
                 │              │
             payment         timeout
              success            │
                 │              │
                 ▼              ▼
          ┌───────────┐    ┌────────┐
          │ SUCCEEDED │    │ FAILED │
          └───────────┘    └────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making the states explicit makes recovery much easier.&lt;/p&gt;




&lt;h1&gt;
  
  
  54. The Hardest Race: Bid vs Auction Expiration
&lt;/h1&gt;

&lt;p&gt;There is a subtle race condition.&lt;/p&gt;

&lt;p&gt;Suppose the auction expires at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;11:00:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At almost exactly the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User A submits a $900 bid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fulfillment Service tries to close the auction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which one wins?&lt;/p&gt;

&lt;p&gt;We need a clearly defined ordering rule.&lt;/p&gt;

&lt;p&gt;A robust approach is to make the final transition conditional in the database.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Close auction only if:

status = ACTIVE
AND expire_at &amp;lt;= now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bid should similarly be accepted only if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = ACTIVE
AND current time &amp;lt; expire_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database transaction / compare-and-set operation determines which state transition wins.&lt;/p&gt;

&lt;p&gt;This is an important example of why final winner selection cannot rely only on cache state.&lt;/p&gt;




&lt;h1&gt;
  
  
  55. Idempotency and Retries
&lt;/h1&gt;

&lt;p&gt;Distributed systems retry requests.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Bid Service
       ↓
Request succeeds
       ↓
Network response lost
       ↓
Client retries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without idempotency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Same logical bid
   ↓
Two database records
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why a client request can include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requestId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Idempotency-Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server can detect that the same logical operation has already been processed.&lt;/p&gt;

&lt;p&gt;This is especially important for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction creation
Bid placement
Payment
Auction state transitions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  56. What Happens If the Auction Service Crashes?
&lt;/h1&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User sends $700 bid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auction Service writes the bid successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB → SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but crashes before updating the cache.&lt;/p&gt;

&lt;p&gt;After recovery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB → $700
Cache → $650
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reconciliation/read-repair mechanism can detect the discrepancy.&lt;/p&gt;

&lt;p&gt;The important design principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The durable operation should be recoverable even if the process dies immediately afterward.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  57. What Happens If Fulfillment Crashes?
&lt;/h1&gt;

&lt;p&gt;Suppose Fulfillment decides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123 should close
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but crashes before completing the transition.&lt;/p&gt;

&lt;p&gt;Another Fulfillment worker can pick it up.&lt;/p&gt;

&lt;p&gt;The final database operation should be conditional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE auction
SET status = PAYMENT_PENDING
WHERE auction_id = ?
AND status = ACTIVE
AND expire_at &amp;lt;= now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only one worker will successfully transition the row.&lt;/p&gt;

&lt;p&gt;This makes the operation idempotent and safe to retry.&lt;/p&gt;




&lt;h1&gt;
  
  
  58. What Happens If Notification Fails?
&lt;/h1&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction → PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but notification delivery fails.&lt;/p&gt;

&lt;p&gt;The auction should not remain stuck simply because an email or push notification failed.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction state
   ↓
Persisted successfully

Notification
   ↓
Async retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notification system can retry independently.&lt;/p&gt;

&lt;p&gt;This is another reason not to put non-critical side effects directly inside the critical transaction.&lt;/p&gt;




&lt;h1&gt;
  
  
  59. What Happens If Payment Succeeds but Auction Isn't Updated?
&lt;/h1&gt;

&lt;p&gt;This is one of the most important recovery cases.&lt;/p&gt;

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

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction DB
   ↓
Still PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Reconciliation Service can detect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment = SUCCESS
Auction = PAYMENT_PENDING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and correct the auction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction → SUCCEEDED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why reconciliation is not an optional afterthought in distributed systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  60. Final Stateless Architecture
&lt;/h1&gt;

&lt;p&gt;The complete stateless design can now be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                              USERS
                                |
                                ▼
                         Load Balancer
                                |
                   ┌────────────┴────────────┐
                   ↓                         ↓
            Auction Service           Bid Update Service
                   |                         |
             ┌─────┴─────┐             SSE Connections
             ↓           ↓                   |
        Auction DB     Cache                 |
             |           |                   |
             |           └──────┐            |
             |                  ↓            |
             |             Fulfillment       |
             |                  |            |
             |                  ↓            |
             |             Auction DB        |
             |                               |
             └──────────────┐                |
                            ↓                |
                         Dispatcher ─────────┘
                            |
                            ↓
                    Bid Update Services
                            |
                            ↓
                         Viewers

Auction Service
       |
       ↓
     Kafka
       |
   ┌───┴──────────────┐
   ↓                  ↓
Notification     Reconciliation
   |
   ↓
Winner

Fulfillment
   |
   ↓
Payment Service
   |
   ├── SUCCESS → SUCCEEDED
   |
   └── TIMEOUT → FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key property is that the Auction Service itself does not keep auction state in memory.&lt;/p&gt;




&lt;h1&gt;
  
  
  61. When Would We Choose the Stateful Design?
&lt;/h1&gt;

&lt;p&gt;The stateless architecture is usually the better default.&lt;/p&gt;

&lt;p&gt;It is easier to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scale
Recover
Deploy
Load balance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stateful design becomes attractive when we need extremely efficient per-auction processing and want all events for one auction handled by the same process.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction A123
   ↓
Stateful server #5
   ↓
All bids for A123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can make ordering easier.&lt;/p&gt;

&lt;p&gt;But now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server #5 fails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replica
WAL
Snapshot
Recovery
Routing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, stateful systems trade simpler per-entity processing for more difficult availability and scaling.&lt;/p&gt;




&lt;h1&gt;
  
  
  62. The Most Important Trade-offs
&lt;/h1&gt;

&lt;p&gt;There is no single perfect architecture.&lt;/p&gt;

&lt;p&gt;The major decisions are:&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateless vs Stateful
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stateless
→ easier scaling and availability

Stateful
→ easier per-auction ordering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache vs Database for Current Bid
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
→ fast

Database
→ authoritative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical design uses both.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSE vs WebSocket
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SSE
→ simple one-way updates

WebSocket
→ richer bidirectional communication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dispatcher vs Direct Coordination Store
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dispatcher
→ cleaner separation

Direct coordination
→ fewer components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache-driven vs DB-driven Fulfillment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
→ fast, lower DB pressure

DB
→ authoritative, more expensive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hybrid approach is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache → identify candidate
DB → verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  63. The Design in One Mental Model
&lt;/h1&gt;

&lt;p&gt;The entire system can be remembered through four responsibilities.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;handles the business operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create auction
Place bid
Update current bid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;keeps the durable truth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction
Bid history
Winner
Payment state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bid Update Service + Dispatcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handles the real-time experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New bid
   ↓
Dispatcher
   ↓
Bid Update Service
   ↓
SSE
   ↓
Viewers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fulfillment + Reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handles time and recovery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction expires
     ↓
Determine winner
     ↓
Payment
     ↓
Success / Failure

Abnormal state
     ↓
Reconcile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  64. A Natural Interview Walkthrough
&lt;/h1&gt;

&lt;p&gt;If you were explaining this system in an interview, the conversation can naturally progress like this.&lt;/p&gt;

&lt;p&gt;Start with the requirements:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Users can create auctions, view active auctions, place bids, and receive real-time updates. An auction closes after one hour without a higher bid. The winner gets ten minutes to pay."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then establish the scale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1B DAU
100K auctions/day
~100M bids/day
10:1 read/write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then identify the core challenge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The most interesting problem is handling real-time updates while maintaining correct winner selection."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then introduce the architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auction Service
Auction DB
Cache
Bid Update Service
Dispatcher
Fulfillment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explain the live path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP bid
   ↓
DB
   ↓
Cache
   ↓
Dispatcher
   ↓
SSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then explain expiration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
   ↓
Fulfillment
   ↓
DB verification
   ↓
Winner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then discuss failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retry
Idempotency
Read repair
Reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stateless vs Stateful
SSE vs WebSocket
Cache vs DB
Dispatcher vs coordination store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a coherent system-design discussion rather than a list of technologies.&lt;/p&gt;




&lt;h1&gt;
  
  
  65. Final Takeaways
&lt;/h1&gt;

&lt;p&gt;The auction system looks simple from the outside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seller lists item
     ↓
Users bid
     ↓
Highest bidder wins
     ↓
Winner pays
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distributed system underneath is much more interesting.&lt;/p&gt;

&lt;p&gt;The most important ideas are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Keep bid history durable and append-only.

2. Keep the current highest bid in a fast cache.

3. Use SSE for efficient one-way live updates.

4. Use a Dispatcher to route updates to the right
   Bid Update Service.

5. Treat the Auction DB as the authoritative source.

6. Allow eventual consistency for live display,
   but use strong consistency when selecting the winner.

7. Use a Fulfillment Service to process auction expiration.

8. Verify the winner against the database before closing
   the auction.

9. Use idempotency and conditional state transitions
   so retries are safe.

10. Use reconciliation to repair abnormal distributed states.

11. Watch for hot auctions and hot cache keys.

12. Stateless architecture is easier to scale and recover;
    stateful architecture can simplify per-auction ordering
    but makes failure recovery harder.

13. SSE is sufficient when the main real-time requirement
    is server-to-client updates; WebSocket is useful when
    richer bidirectional communication is needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deepest system-design lesson is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The live bidding experience can tolerate some temporary inconsistency, but the final winner cannot.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single distinction explains why the system combines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
+
Real-time events
+
SSE
+
Database
+
Scheduler
+
Reconciliation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component solves a different part of the problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  66. Reference
&lt;/h1&gt;

&lt;p&gt;The design and assumptions in this article are based on the free Coding Monkey article:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Design Auction System&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pyemma.github.io/How-to-design-auction-system/" rel="noopener noreferrer"&gt;https://pyemma.github.io/How-to-design-auction-system/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original article discusses the stateless and stateful designs, live bid routing, SSE, Dispatcher, cache/database consistency, Fulfillment Service, scalability, availability, and SSE/WebSocket trade-offs.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>scalability</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
