Designing a Live Shopping Platform with Real-Time Inventory

Designing a Live Shopping Platform with Real-Time Inventory

Designing a Live Shopping Platform with Real-Time Inventory

A complete, ground-up architecture for a system where a creator sells products live on video, thousands of viewers watch and chat in real time, and every purchase must update inventory instantly and correctly — even when ten thousand people click “Buy” on the same limited-stock item within the same second.

01

Introduction and History

Imagine a shopping channel from the 1990s — a presenter holding up a blender, talking about it, and a phone number scrolling at the bottom of the screen for viewers to call and order. That idea, home shopping television, is decades old. What is new is what happens when you take that same idea and rebuild it on top of the internet, mobile phones, and modern distributed systems. Instead of a phone call, a viewer taps a screen. Instead of one broadcast reaching a few million TV sets, a stream can reach millions of phones anywhere in the world. And instead of a warehouse worker manually tracking how many blenders are left, a computer system has to know the exact stock count, update it in real time, and never let two people buy the last unit at the same time.

This combination — live video plus e-commerce plus real-time inventory — is usually called live shopping or live commerce. A creator or brand goes live on camera, shows off products, answers questions from viewers in a chat window, and viewers can buy those products without ever leaving the stream. The stream, the chat, the product catalog, and the checkout system all have to work together, in real time, at large scale.

Live commerce did not start in Silicon Valley. It became massive first in China, where platforms such as Taobao Live (part of Alibaba) turned livestream selling into a business worth many billions of dollars a year, with some individual streamers selling more merchandise in a single evening than a mid-size retail chain sells in a month. Seeing that success, western platforms followed: Amazon Live let sellers and influencers demonstrate products on video next to a live “buy” button; TikTok built TikTok Shop directly into its short-video and live-streaming app; Instagram and Facebook experimented with shopping tags inside live video; and newer, focused platforms like Whatnot built their entire business around live auctions and live selling for collectibles, fashion, and hobbyist communities.

From a systems point of view, live shopping is a fascinating problem because it forces three very different engineering worlds to work together at the same time:

World 1

Real-time video streaming

The same kind of low-latency video delivery problem that YouTube Live, Twitch, and video conferencing tools solve.

World 2

High-concurrency checkout

The same kind of “flash sale” problem that ticketing sites and sneaker drop sites solve, where thousands of people try to buy a handful of units at the exact same moment.

World 3

Real-time social interaction

Live chat, reactions, and viewer counts — the same kind of problem that chat applications and multiplayer games solve.

Simple analogy

Think of live shopping as running a physical in-store demo, a public auction, and a cash register all at once — except the “store” has millions of people standing in it at the same moment, all reaching for the same three items on the shelf, and the cash register has to say “sold out” to everyone else the instant the last item is taken, without ever double-selling it.

In this tutorial we will design such a platform from scratch, piece by piece. We will cover the video pipeline, the microservices that manage products and orders, the message queue that connects everything, the caching layer that makes inventory checks fast, the database design that keeps orders and stock counts correct, and the operational concerns — security, monitoring, deployment — that keep the whole system running smoothly during a launch event that might attract a huge, sudden spike of traffic in the space of a few minutes.

02

Problem and Motivation

Before drawing any boxes and arrows, we need to be precise about what problem we are actually solving, and why it is hard. It is easy to say “let creators sell products on a livestream.” It is much harder to say exactly what breaks if we build this carelessly.

2.1 What makes this different from normal e-commerce?

A normal online store, like a typical product listing page, has a fairly gentle traffic pattern. People browse whenever they want, over hours or days. A live shopping event compresses all of that demand into a very short, very intense window — often just a few minutes — because the creator says on camera, “I have only 200 units of this jacket, link is live now,” and thousands of viewers, all watching the same video at the same moment, react at the same moment.

Normal E-CommerceLive Shopping
Traffic spreads out over hours/daysTraffic spikes in seconds, tied to a spoken moment on camera
Users browse independentlyUsers watch the same video frame, react together
Stock usually lasts hoursLimited-edition drops can sell out in under a second
No live social layerLive chat, reactions, and viewer count must update in real time
Checkout latency of 1–2s is fineEven small delays cause visible “still processing” frustration while stock disappears

2.2 The core hard problems

HP 1

The Overselling Problem

If 5,000 people click “Buy” on an item with 50 units left, at the exact same second, how do we guarantee that exactly 50 orders succeed and 4,950 people get an honest “sold out” — without the database falling over, and without accidentally selling 51 or 60 units because two servers both thought there was stock left?

HP 2

The Low-Latency Video Problem

Video has to travel from the creator’s phone or camera, through the internet, to potentially millions of viewers’ phones, with only a few seconds of delay. If the delay is too large, viewers see the item highlighted long after it is already sold out, and they buy something that no longer exists.

HP 3

The Chat & Engagement Scale Problem

A popular stream can have hundreds of thousands of concurrent viewers, all able to send chat messages, likes, and reactions. Broadcasting all of that to everyone, in real time, without melting the servers, is its own distributed systems challenge.

HP 4

Consistency vs. Speed Tension

Strict correctness (never oversell) usually means locking or coordinating, which can slow things down. But slow checkout during a flash-sale moment is unacceptable. The design has to be both fast and correct.

HP 5

“Everyone Is Watching the Same Clock”

Unlike a normal store, thousands of viewers are quite literally reacting to the same 2-second window of video. This creates a genuine thundering herd — a sudden, synchronized wave of demand — which is much harder to smooth out than ordinary organic traffic.

Real-life analogy

Picture a single ticket counter at a train station with 50 tickets left for a very popular train, and a loudspeaker just announced “last 50 tickets, on sale now” to a crowd of 5,000 people simultaneously. Everyone rushes the counter at once. A good system is the equivalent of instantly forming a fair, fast-moving queue that hands out exactly 50 tickets and turns everyone else away politely and immediately — not a crowd crush at a single window.

2.3 Business motivation

Beyond the technical challenge, the business case is strong: live shopping combines entertainment and commerce (“shoppertainment”), which tends to produce far higher conversion rates than static product pages, because viewers get real-time answers to their questions, social proof from other viewers buying at the same time, and a sense of urgency from limited stock and a ticking clock. Getting the system right directly translates into revenue; getting it wrong — a stream that lags, a checkout that fails, or a stock count that is wrong — directly damages trust and sales in a very visible, very public way, in front of a live audience.

💬
What an interviewer may ask

“Why can’t we just use a normal e-commerce checkout flow for this?” A strong answer explains the compressed, synchronized demand spike unique to live events, and why classic optimistic web-scale patterns (eventual consistency, lazy stock updates) can cause visible overselling that is unacceptable when thousands of eyes are on the same countdown at once.

03

Requirements

3.1 Functional Requirements

FR1

Broadcast & Watch

Creators can start, run, and end a live video stream from a phone, camera, or streaming software. Viewers can join on web or mobile with minimal startup delay.

FR2

Spotlight Products

Creators can “pin” or “spotlight” a product during the stream, showing it as buyable to viewers.

FR3

Instant Buy

Viewers can buy a spotlighted product instantly, without leaving the stream, in a few taps.

FR4

Live Inventory

Inventory counts must update in real time and be visible to all viewers (“12 left” ticking down live).

FR5

Chat & Engagement

Viewers can chat, react (likes/hearts), and see a live viewer count. Creators and moderators can moderate chat.

FR6

Order Integration

Orders integrate with payment processing, shipping, and order-history systems.

FR7

Replay (VOD)

Stream replays should be available after the live event ends, ideally still shoppable.

FR8

Analytics

Creators and platform operators need real-time dashboards of viewers, sales, and conversion.

3.2 Non-Functional Requirements

RequirementTargetWhy it matters
Video glass-to-glass latency2–5 secondsViewers must see the same “moment” as everyone else, close to real time
Inventory accuracyZero overselling, everOverselling means broken promises, refunds, and reputational damage
Checkout response time< 300ms p99 for the buy actionFlash-sale UX must feel instant or users assume it failed and retry, worsening load
Concurrent viewers per streamUp to millionsViral streams and celebrity creators can draw huge simultaneous audiences
Availability99.95%+ during live eventsA live event cannot be “rescheduled” if the system goes down mid-stream
Chat/reaction fan-outSub-second delivery to all viewersChat that arrives late feels broken and kills engagement
Consistency for orders/paymentsStrong consistency (ACID)Money and stock cannot be “eventually correct” — they must be exactly correct
Consistency for viewer count/chatEventual consistency is fineBeing off by a few viewers or a chat message arriving 200ms late causes no real harm
💡
Key design insight

Not every part of this system needs the same consistency guarantee. Money and stock need strong consistency. Viewer counts and “likes” can be approximate and eventually consistent. Recognizing which parts need which guarantee is the single most important decision in this whole design — and it is exactly the kind of judgment call interviewers want to see you make explicitly, rather than applying one consistency model everywhere.

3.3 Back-of-the-envelope scale estimation

Let’s reason about scale, since it drives almost every later decision:

Peak viewers

0.5M–2M

Concurrent viewers on a single popular stream for a viral celebrity event.

Buy clicks

10s of K/sec

Peak “buy” clicks in the first second after a product drop for a hyped, limited-stock item.

Chat msgs

1000s/sec

Chat messages per second on a huge stream at peak; after fan-out to all viewers, tens of millions of message deliveries per second.

Concurrent streams

100s–1000s

Number of concurrent live streams platform-wide, each independently scaling.

These numbers tell us immediately that we cannot rely on a single database instance for inventory, we cannot broadcast chat by looping over a list of users in application code, and we cannot serve video by streaming directly from the creator’s device to every viewer — we need a CDN.

04

Architecture and Components

Let’s now design the system piece by piece. We will split it into three cooperating “planes”: the video plane (getting the creator’s camera feed to millions of viewers), the commerce plane (product catalog, inventory, orders, payments), and the real-time engagement plane (chat, reactions, live viewer counts). All three sit behind a shared edge layer of load balancers, API gateway, and CDN.

4.1 High-Level Architecture Diagram

Every box below is labeled with exactly what kind of component it is, so the role of each piece in the request path is unambiguous.

Viewer Mobile/Web App Creator App / Encoder (OBS, RTMP) CDN Edge NodesHLS/DASH + static assets Load Balancer L7NGINX/Envoy + TLS API Gatewayauthn, rate limit Video Plane Stream Ingest Transcoding Packaging (HLS/DASH) Commerce Plane Product Catalog Inventory Service Order Service Payment Service Shipping / Fulfillment Real-Time Engagement Chat Service (WebSocket) Presence / Viewer Count Notification Service Message Broker (Kafka)order/inventory/stream events, decoupled fan-out Cache Cluster (Redis)hot inventory keys, sessions, product cache Order DBSharded PostgreSQL (ACID) Catalog DBDocument Store (MongoDB) Search IndexElasticsearch Analytics WarehouseBigQuery / Snowflake Observability (Prometheus + Grafana + OpenTelemetry + ELK)
Fig 4.1 — Three-plane architecture: Video (ingest → transcode → package → CDN), Commerce (Catalog/Inventory/Order/Payment/Shipping), and Real-Time Engagement (Chat/Presence/Notifications), connected through Kafka and Redis.

At first glance this looks like a lot of boxes, but each one exists to solve exactly one of the hard problems from Section 2. Let’s walk through every component, what it is, why it exists, and how it maps to something familiar.

4.2 Edge Layer

4.2.1 CDN (Content Delivery Network)

What it is: A globally distributed network of edge servers that cache and serve content physically close to each viewer.

Why it exists: Video is heavy. If every viewer pulled video directly from our origin servers, our data center’s network link would be overwhelmed in seconds by a stream with a million viewers. A CDN instead lets each viewer download video segments from a nearby edge node, so our origin only needs to serve a handful of edge nodes, not millions of viewers directly.

Analogy

Instead of one water tank supplying every house in a country directly through one pipe, you build local water towers in every city, filled from the main tank, so each house draws from a nearby tower. The main tank only needs to keep the towers full, not serve every house individually.

In this system: The CDN caches the video’s HLS/DASH segments and also serves static assets like product images. Popular CDNs used for this include Akamai, Cloudflare, Amazon CloudFront, and Fastly.

4.2.2 Load Balancer (Layer 7)

What it is: A component that sits in front of a group of servers and distributes incoming requests across them, using application-level information (HTTP path, headers) since we labeled it Layer 7.

Why it exists: No single server can handle millions of requests. The load balancer spreads traffic evenly, performs health checks to route around failed instances, and terminates TLS so backend services don’t each need to manage certificates.

Analogy

A restaurant host who looks at how busy each section of the restaurant is, and seats new guests at whichever table’s server currently has capacity, instead of guests randomly choosing a table and overloading one section.

In this system: NGINX or Envoy sit at the edge, terminating TLS and load-balancing across API Gateway instances. During a flash-sale spike, the load balancer’s health checks and connection-draining logic keep failing instances out of rotation immediately.

4.2.3 API Gateway

What it is: A single, unified entry point for all client requests into the backend microservices.

Why it exists: Without a gateway, every client would need to know the address of every individual microservice, and every microservice would need to reimplement authentication, rate limiting, and request logging. The gateway centralizes these cross-cutting concerns.

Analogy

A hotel’s front desk. Guests don’t wander the building looking for housekeeping or room service directly — they go to the front desk, which checks who they are (authentication) and routes their request to the right department.

In this system: The API Gateway authenticates the viewer’s session token, applies per-user and per-IP rate limiting (critical during a “buy now” surge to stop bots and retries from overwhelming the Order Service), and routes /catalog/* to the Catalog Service, /inventory/* to the Inventory Service, /orders/* to the Order Service, and upgrades /chat connections to WebSocket and routes them to the Chat Service.

💬
What an interviewer may ask

“Why put both a load balancer and an API gateway in the path — isn’t that redundant?” A good answer: the load balancer operates at the transport/HTTP routing level across gateway replicas for availability and scaling; the API gateway operates at the application level, handling auth, rate limiting and business routing. They solve different problems and commonly coexist in production systems.

4.3 Video Plane Components

4.3.1 Stream Ingest Service

What it is: The entry point that receives the live video/audio feed from the creator’s camera or streaming software, typically over the RTMP or WebRTC protocol.

Why it exists: The creator’s raw camera feed needs a dedicated, always-available receiving endpoint that can handle unreliable mobile networks, reconnect gracefully, and hand the stream off for processing.

Analogy

A television studio’s satellite uplink receiver — the first point where a broadcast truck’s signal enters the professional broadcast pipeline.

Production example: Twitch and YouTube Live use dedicated ingest server fleets distributed by geography so a creator connects to a nearby ingest point, minimizing upload latency before transcoding even starts.

4.3.2 Transcoding Service

What it is: Converts the single incoming high-bitrate stream into multiple quality “renditions” (for example 1080p, 720p, 480p, 240p).

Why it exists: Viewers have wildly different network conditions — a viewer on fast Wi-Fi and a viewer on patchy mobile data cannot both be served the exact same video bitrate. Transcoding produces several versions so the player on each device can pick (and dynamically switch) the best one, a technique called adaptive bitrate streaming (ABR).

Analogy

A publisher printing the same book in hardcover, paperback, and large-print editions, so each reader can pick the format that fits their needs, all from one original manuscript.

4.3.3 Packaging Service

What it is: Slices each transcoded rendition into small segments (typically 2–6 seconds) and produces a manifest file, packaged as HLS (HTTP Live Streaming) or DASH (Dynamic Adaptive Streaming over HTTP).

Why it exists: Video players can’t efficiently stream one giant continuous file over HTTP. Breaking video into small segments lets players fetch, cache, and switch quality level segment-by-segment, and lets the CDN cache each small segment independently.

4.4 Commerce Plane Components

4.4.1 Product Catalog Service

What it is: Owns product data — titles, descriptions, images, prices, variants (size/color) — and serves fast reads to the client and to other services.

Why it exists: Product data changes rarely compared to inventory, and is read constantly (every viewer loads product cards). Separating it from inventory lets us cache it aggressively without worrying about staleness on the number that actually matters for correctness (stock count).

Analogy

A restaurant’s printed menu (catalog: dish name, description, price) versus the kitchen’s live count of how many portions of today’s special are left (inventory). The menu can be printed once and reused all night; the count of remaining portions must be checked fresh every time.

4.4.2 Inventory Service

What it is: The single source of truth for how many units of each product/variant are available, and the component responsible for atomically reserving and decrementing stock as orders are placed.

Why it exists: This is the most safety-critical service in the whole system. It exists as an isolated service (rather than a field on the Catalog Service) specifically so it can be optimized, scaled, and locked down for one job: never letting stock go negative, no matter how much concurrent load hits it.

We will look at exactly how it achieves atomic decrements under extreme concurrency in Section 5.

4.4.3 Order Service

What it is: Manages the lifecycle of an order — created, payment pending, paid, fulfilled, cancelled, refunded — and orchestrates the multi-step process of turning a “Buy” click into a confirmed purchase.

Why it exists: A purchase touches multiple systems (inventory, payment, shipping). The Order Service coordinates these steps as a single logical transaction using patterns like Saga (explained in Section 15), so that a failure partway through — for example, a payment decline after inventory was reserved — is always cleanly rolled back.

4.4.4 Payment Service

What it is: Handles charging the customer’s payment method, typically by calling out to a PCI-compliant third-party payment processor (Stripe, Razorpay, Adyen, PayPal) rather than handling raw card data itself.

Why it exists: Isolating payment logic into one service shrinks the “PCI compliance boundary” — the part of the system that must meet strict card-data security standards — to the smallest possible surface area, instead of spreading sensitive payment handling across the whole codebase.

4.4.5 Shipping / Fulfillment Service

What it is: Triggered after a successful payment, this service creates fulfillment tasks — packing, warehouse pick lists, courier handoff, tracking numbers.

Why it exists: Fulfillment is inherently asynchronous (a warehouse worker packs a box over minutes or hours, not milliseconds) so it is decoupled from the fast, synchronous checkout path via the message broker.

4.5 Real-Time Engagement Plane

4.5.1 Chat Service (WebSocket Gateway)

What it is: Manages persistent WebSocket connections with every connected viewer, receiving chat messages and reactions and fanning them out to all other viewers of that stream.

Why it exists: HTTP request/response is the wrong model for “many people need to see the same message within milliseconds.” WebSockets keep an open, bidirectional connection so the server can push messages instantly instead of clients repeatedly polling.

Analogy

A walkie-talkie channel that everyone in the group is tuned into, versus repeatedly calling each person’s phone to read them the latest message.

Scaling note: Chat servers are typically organized so all connections for a given stream’s “room” are grouped, and a pub/sub layer (Redis Pub/Sub or a Kafka topic per popular room) fans messages out across many chat server instances, since a single server cannot hold a million concurrent WebSocket connections for one viral stream.

4.5.2 Presence / Viewer-Count Service

What it is: Tracks who is currently watching each stream and produces the “12,458 watching” counter.

Why it exists: An exact, strongly consistent count would require coordinating every single connect/disconnect event, which is unnecessary overhead for a number that is inherently approximate and constantly changing. This service uses approximate counting (for example, periodically aggregated counts from each chat server shard) which is far cheaper and entirely acceptable for this use case.

4.5.3 Notification Service

What it is: Sends push notifications, SMS, or email — for example, “Your favorite creator just went live,” or “Your order has shipped.”

Why it exists: Like fulfillment, notifications are asynchronous and non-critical-path, so they are triggered by events on the message broker rather than blocking any user-facing request.

4.6 Shared Infrastructure

4.6.1 Message Broker (Kafka)

What it is: A distributed, append-only log system that lets services publish events (like “order placed” or “stock updated”) and lets other services subscribe and react, without the publisher needing to know who is listening.

Why it exists: Without an event bus, every service would need direct point-to-point calls to every other interested service, creating a tangled, fragile web of synchronous dependencies. The broker decouples producers from consumers, absorbs bursts of traffic (acting as a buffer), and lets us add new consumers (like a new analytics pipeline) without touching existing services.

Analogy

A public notice board in a village square. Someone posts a notice (“new item in stock”) once; anyone interested walks by and reads it whenever they want, without the notice-poster needing to individually tell every villager.

4.6.2 Cache Cluster (Redis)

What it is: An in-memory key-value data store used to hold frequently accessed, latency-sensitive data — hot product info, session tokens, and critically, live inventory counters.

Why it exists: Reading and writing a stock counter from a disk-based relational database, thousands of times per second, is far too slow for a flash-sale moment. Redis keeps the “hot” numbers in memory and supports atomic operations, which we rely on heavily in Section 5.

4.6.3 Observability Stack

What it is: The combination of metrics collection (Prometheus), dashboards (Grafana), distributed tracing (OpenTelemetry/Jaeger), and centralized logging (the ELK stack: Elasticsearch, Logstash, Kibana).

Why it exists: With a dozen microservices cooperating on a single purchase, when something goes wrong you need to trace one request’s full journey across every service it touched — this is covered fully in Section 11.

05

Internal Working

Now let’s zoom into the two hardest internal mechanics: how a “Buy” click is processed without overselling, and how the video actually gets from a phone camera to a million screens with only a few seconds of delay.

5.1 The Purchase Path, Step by Step

  1. Viewer taps “Buy” on the spotlighted product inside the stream player.
  2. Request hits the Load Balancer, then the API Gateway, which checks the auth token and applies rate limiting.
  3. The Order Service receives the purchase intent and calls the Inventory Service to reserve one unit — not yet a full decrement, but a short-lived hold (see 5.2).
  4. If the reservation succeeds, the Order Service creates a pending order record and calls the Payment Service to charge the customer.
  5. If payment succeeds, the Order Service confirms the reservation (now a real decrement) and marks the order as paid; it publishes an OrderPlaced event to Kafka.
  6. If payment fails, the Order Service releases the reservation back to available stock, and the order is marked failed.
  7. Downstream consumers of the OrderPlaced event (Shipping Service, Notification Service, Analytics Warehouse) react independently and asynchronously.
  8. The updated stock count is pushed to all viewers of that stream via the Presence/Chat pub-sub channel, so everyone sees “3 left” tick down live.

5.2 Solving the Overselling Problem: Atomic Reservation

The naive approach — “read the stock count, check if it’s greater than zero, then write count-1” — is broken under concurrency. If a thousand requests all read “5” before any of them writes, all thousand may believe they succeeded. This is called a race condition, and it’s exactly what causes overselling.

The fix is to make the check-and-decrement a single, indivisible (atomic) operation, so no two requests can ever interleave their reads and writes. Redis makes this simple with a Lua script, which Redis guarantees runs atomically, from start to finish, with no other command executing in between:

atomic-decrement.lua
-- KEYS[1] = inventory key, e.g. "inventory:product:12345"
-- Returns: 1 if reserved successfully, 0 if out of stock, -1 if key missing

local stock = tonumber(redis.call('GET', KEYS[1]))
if stock == nil then
    return -1  -- product not found in cache
end
if stock > 0 then
    redis.call('DECR', KEYS[1])
    return 1
else
    return 0
end

Here is how the Inventory Service (written in Java, using the Spring framework and Lettuce/Jedis Redis client) calls this script atomically:

InventoryReservationService.java
@Service
public class InventoryReservationService {

    private final RedisScript<Long> decrementScript;
    private final StringRedisTemplate redisTemplate;
    private final KafkaTemplate<String, InventoryEvent> kafkaTemplate;

    public InventoryReservationService(StringRedisTemplate redisTemplate,
                                        KafkaTemplate<String, InventoryEvent> kafkaTemplate) {
        this.redisTemplate = redisTemplate;
        this.kafkaTemplate = kafkaTemplate;
        this.decrementScript = RedisScript.of(
            new ClassPathResource("scripts/atomic-decrement.lua"), Long.class);
    }

    public ReservationResult reserve(String productId, String orderId) {
        String key = "inventory:product:" + productId;
        Long result = redisTemplate.execute(decrementScript, List.of(key));

        if (result == null || result == -1L) {
            throw new ProductNotCachedException(productId);
        }
        if (result == 0L) {
            return ReservationResult.OUT_OF_STOCK;
        }

        // Reservation succeeded - record a short-lived hold so we can
        // roll back cleanly if payment fails.
        String holdKey = "hold:" + orderId;
        redisTemplate.opsForValue().set(holdKey, productId, Duration.ofMinutes(2));

        // Publish so live viewers see the count drop.
        kafkaTemplate.send("inventory-events",
            new InventoryEvent(productId, InventoryEventType.RESERVED, orderId));

        return ReservationResult.RESERVED;
    }

    public void release(String orderId) {
        String holdKey = "hold:" + orderId;
        String productId = redisTemplate.opsForValue().get(holdKey);
        if (productId != null) {
            redisTemplate.opsForValue().increment("inventory:product:" + productId);
            redisTemplate.delete(holdKey);
            kafkaTemplate.send("inventory-events",
                new InventoryEvent(productId, InventoryEventType.RELEASED, orderId));
        }
    }
}

Why does this scale so well? Because Redis is single-threaded for command execution on a given key, every DECR-style operation against one key is naturally serialized — there is no possibility of two requests interleaving on the same counter, and no explicit application-level lock is needed at all.

Common mistake

Doing the stock check in application code with a separate GET followed by a separate SET, even against Redis. That reintroduces the race condition, because another request’s write can slip in between your GET and your SET. Always use an atomic primitive (Lua script, or Redis’s built-in DECR/WATCH/transactions) for check-and-decrement logic.

5.3 Keeping Redis and the Database in Sync

Redis gives us speed, but it is a cache, not the ultimate source of truth for financial-grade correctness — a cache node could restart and lose data. So the design uses a two-tier approach:

  • Fast path (hot): Redis atomic decrement handles the flash-sale-speed reservation check.
  • Durable path (source of truth): The Order Service, once payment succeeds, writes the final, authoritative decrement into the relational database inside the same transaction as the order confirmation, using optimistic locking as a second safety net.

Here’s the JPA entity with optimistic locking as a defense-in-depth safety net, in case Redis and the database ever drift (for example after a cache failover):

InventoryRecord.java
@Entity
@Table(name = "inventory")
public class InventoryRecord {

    @Id
    private String productId;

    @Column(nullable = false)
    private int stockCount;

    @Version   // JPA optimistic locking column
    private long version;

    public void decrementStock() {
        if (stockCount <= 0) {
            throw new InsufficientStockException(productId);
        }
        this.stockCount -= 1;
    }
    // getters/setters omitted
}

@Service
public class InventoryPersistenceService {

    @Retryable(value = OptimisticLockException.class, maxAttempts = 5,
               backoff = @Backoff(delay = 20, multiplier = 2))
    @Transactional
    public void confirmDecrement(String productId) {
        InventoryRecord record = inventoryRepository.findById(productId)
            .orElseThrow(() -> new ProductNotFoundException(productId));
        record.decrementStock();
        inventoryRepository.save(record); // throws OptimisticLockException on version conflict
    }
}

If two transactions somehow race at the database layer, JPA’s @Version column causes the losing transaction’s commit to fail with an OptimisticLockException, which we retry with exponential backoff. In practice, because Redis already serialized the reservation upstream, this database-level conflict should be rare — it exists purely as a correctness backstop, not the primary defense.

5.4 The Video Path: Glass-to-Glass

“Glass-to-glass latency” means the time from light hitting the creator’s camera lens to light leaving the viewer’s screen. Here is that path:

Camera / Encoder Ingest Svc Transcoding Packaging (HLS/DASH) CDN Edge Viewer Player RTMP/WebRTC chunk (~2s) raw frame buffer encode into 1080p/720p/480p encoded renditions slice into 2–6s segments + manifest push/pull segment + manifest segment delivered (nearest edge) ABR selects best rendition Total glass-to-glass target: 2–5 seconds
Fig 5.1 — Video glass-to-glass path: Camera → Ingest (RTMP/WebRTC) → Transcoding (ABR renditions) → Packaging (HLS/DASH slicing) → CDN edge → Viewer player.

Two techniques keep this fast: using small segment sizes (so the player doesn’t wait for a large chunk to finish encoding before it can start playing), and using WebRTC instead of RTMP for the sub-second-latency “interactive” tier some platforms offer for creator-to-moderator communication, while the bulk of viewers still consume the slightly higher-latency but far more scalable HLS/DASH path through the CDN.

💬
What an interviewer may ask

“Why not deliver video to every viewer directly over WebRTC for the lowest possible latency?” WebRTC peer connections don’t scale well to millions of simultaneous viewers because each connection consumes dedicated server resources; HLS/DASH over a CDN scales to massive audiences because segments are cacheable, shareable files, at the cost of a few extra seconds of latency. Most live shopping platforms accept 2–5s in exchange for that scalability.

06

Data Flow and Lifecycle

Let’s trace one complete “life of a purchase” as a sequence diagram, showing every component from Section 4 working together.

Viewer LB Gateway Order Svc Inventory (Redis) Payment Order DB Kafka Shipping Chat POST /buy forward authn + rate limit createOrder reserve atomic Lua DECR RESERVED charge(user, amount) PAYMENT_SUCCESS commit order (PAID) + confirm decrement publish OrderPlaced 200 OK order confirmed consume → fulfillment task consume InventoryUpdated WebSocket push “3 left”
Fig 6.1 — Purchase happy path: Viewer → LB → Gateway → Order Svc → Inventory (atomic Redis decrement) → Payment → Order DB → Kafka → async fulfillment + live stock broadcast.

6.1 The Rollback Path (Payment Failure)

Correctness also requires handling the unhappy path cleanly. If payment fails after inventory was reserved, we must release the hold immediately so the unit becomes available to the next viewer:

Order Service Inventory Service Payment Service Kafka reserve(productId, orderId) RESERVED charge(user, amount) PAYMENT_DECLINED release(orderId) atomic INCR + delete hold publish InventoryUpdated (stock +1) order status = FAILED
Fig 6.2 — Rollback path on payment failure: compensating release atomically increments stock back and publishes an InventoryUpdated event so viewers see the unit reappear.

Note that the two-minute hold TTL set on the Redis key in Section 5.2 acts as a safety net too: even if the Order Service crashes mid-flow and never explicitly calls release(), the hold key expiring automatically frees the reservation, and a background reconciliation job re-adds the unit to the visible stock count if it detects an orphaned hold.

6.2 Product Lifecycle During a Live Event

StateTriggerVisible to viewers as
ScheduledCreator adds product to stream lineup before going liveNot shown yet
SpotlightedCreator taps “feature this product” on cameraBuy button + live stock count appear
SellingOrders being placedStock count ticking down in real time
Sold OutStock count reaches 0Buy button disabled, “Sold Out” badge
UnspotlightedCreator moves to next productRemoved from active buy panel, still browsable
07

Advantages, Disadvantages and Trade-offs

7.1 Advantages of This Architecture

Advantage

Isolation of the safety-critical path

Because Inventory and Order are separate, focused services, we can lock down, test, and scale exactly the part of the system that must never be wrong, without over-engineering the rest.

Advantage

Independent scaling

Video traffic, chat traffic, and checkout traffic scale completely differently during an event. Splitting them into separate services means we can add ten times more chat server capacity without touching the Order Service at all.

Advantage

Resilience through decoupling

If the Shipping Service is temporarily down, purchases still succeed — the Kafka event simply waits in the topic until Shipping recovers. The customer-facing checkout path is not blocked by a slower downstream step.

Advantage

Reusable video pipeline

The ingest/transcode/package pipeline is generic; it doesn’t know or care about products or checkout, so the same pipeline can power live shopping, regular live streaming, or even video-on-demand replays.

7.2 Disadvantages and Costs

Cost

Operational complexity

A dozen independently deployable services, a message broker, a cache cluster, and a CDN is a lot more moving parts than a single monolithic web app — more things that can fail, more infrastructure to monitor.

Cost

Eventual consistency surprises

Because chat, presence, and cross-service state propagate via events, there is always a small window (milliseconds, but nonzero) where different parts of the system see slightly different pictures of reality — for example, the stock counter briefly showing “1 left” to one viewer after it has actually sold out for another.

Cost

Cross-service latency

A purchase touches Order, Inventory, and Payment services in sequence — each network hop adds latency compared to a single monolithic database transaction.

Cost

Testing & debugging difficulty

Reproducing a bug that only appears under real flash-sale-level concurrency, across several services, is much harder than debugging a single-process application.

7.3 Key Trade-off Decisions

DecisionChosen approachAlternativeWhy we chose this
Inventory consistencyStrong: Redis atomic ops + DB optimistic lockingEventually-consistent countersOverselling is unacceptable; correctness beats raw throughput here
Viewer countApproximate, eventually consistentExact real-time count via central coordinatorExactness is not needed and would be far too costly at scale
Video deliveryCDN-based HLS/DASH, 2–5s latencyDirect WebRTC to every viewer, sub-second latencyCDN scales to millions; direct WebRTC to millions of viewers is not feasible
Service communicationEvent-driven (Kafka) for non-critical-path stepsSynchronous calls everywhereKeeps the checkout critical path fast; failures downstream don’t block purchase
Payment handlingDelegate to third-party PCI-compliant processorBuild in-house card processingDrastically reduces compliance burden and security risk
08

Performance and Scalability

8.1 Scaling the Video Path

8.1.a

CDN edge caching

The vast majority of viewer requests never reach our origin infrastructure at all — they are served from geographically nearby CDN edge nodes, which is the single biggest scalability lever for video.

8.1.b

Adaptive bitrate

By offering multiple renditions, we avoid wasting bandwidth on viewers with weak connections and avoid degrading quality unnecessarily for viewers with strong ones.

8.1.c

Horizontal scaling of transcoding

Transcoding is CPU/GPU intensive; the Transcoding Service runs as a horizontally scalable, stateless fleet, often using hardware-accelerated encoding (NVIDIA GPUs or dedicated ASICs) to process many concurrent streams.

8.2 Scaling the Checkout Path

8.2.a

Sharding inventory by product ID

Since each product’s stock counter lives at an independent Redis key, we can shard the Redis cluster by product ID (using Redis Cluster’s hash slots) so that a hot product’s traffic is isolated to specific shards rather than overwhelming a single node — and different hot products spread naturally across different shards.

8.2.b

Read/write separation for Catalog

Product reads (which vastly outnumber writes) are served from cache and read replicas; writes go to the primary.

8.2.c

Queueing at the edge for extreme drops

For an ultra-hyped, ultra-limited drop (say, 50 units against a million interested viewers), some platforms add a lightweight “virtual waiting room” in front of checkout — a fair, first-come queue implemented with a Redis sorted set — so the Order Service only ever receives a manageable, throttled stream of purchase attempts instead of the entire spike at once.

8.2.d

Rate limiting & idempotency keys

The API Gateway rate-limits repeated “Buy” taps from the same user (common when users panic-tap because the UI feels slow), and every purchase request carries a client-generated idempotency key so a retried request never creates a duplicate order.

8.3 Scaling Chat and Presence

8.3.a

Room-based sharding

Each stream’s chat is its own “room,” and rooms are distributed across many Chat Service instances, so one viral stream’s chat load doesn’t need to fan out from a single process.

8.3.b

Pub/Sub fan-out

A message posted to a room is published once to a Redis Pub/Sub channel (or a Kafka topic) and every Chat Service instance holding connections for that room subscribes and pushes to its local WebSocket connections — this avoids the sender needing to know about every individual connection.

8.3.c

Sampling for extreme scale

On the very largest streams, some platforms deliberately sample or batch chat messages (showing a representative subset rather than literally every message to every viewer) to keep client-side rendering and network usage manageable.

Analogy for sharding

Instead of one supermarket checkout line for the entire city, you open many checkout lanes, and assign each shopper to a lane based on some rule (like their cart’s barcode range). Each lane handles its own smaller crowd independently, so the whole system moves faster than if everyone queued at a single register.

💬
What an interviewer may ask

“How would you handle a single product that is so hyped it becomes a hot key even after sharding?” Good answers mention: further splitting that single product’s counter into N sub-counters (e.g., 10 counters of 5 units each) that requests are randomly assigned to, reducing contention on any one key, then reconciling at the end; or introducing a queue/waiting-room in front of that specific product.

09

High Availability and Reliability

9.1 Redundancy at Every Layer

LayerHA strategy
CDNGlobally distributed edge nodes; automatic failover to next-nearest edge on node failure
Load BalancerDeployed in active-active pairs across availability zones
MicroservicesMultiple stateless replicas per service, spread across zones, behind health-checked load balancing
Redis cacheRedis Cluster with primary-replica shards; automatic failover promotes a replica if a primary node dies
Order DB (PostgreSQL)Primary with synchronous standby replica for zero data loss on failover, plus asynchronous cross-region replicas for disaster recovery
KafkaMulti-broker cluster with topic replication factor of 3, tolerating broker failures without data loss
Stream ingestCreator app configured with a backup ingest endpoint in a second region if primary ingest is unreachable

9.2 Resilience Patterns in Application Code

Even with redundant infrastructure, individual calls between services can still fail or time out. We use the circuit breaker pattern to prevent one failing dependency from cascading into a full outage:

PaymentClient.java
@Service
public class PaymentClient {

    private final CircuitBreaker circuitBreaker;
    private final RestClient restClient;

    public PaymentClient(CircuitBreakerRegistry registry, RestClient restClient) {
        this.circuitBreaker = registry.circuitBreaker("payment-service");
        this.restClient = restClient;
    }

    public PaymentResult charge(ChargeRequest request) {
        Supplier<PaymentResult> call = CircuitBreaker
            .decorateSupplier(circuitBreaker, () ->
                restClient.post()
                    .uri("/v1/charges")
                    .body(request)
                    .retrieve()
                    .body(PaymentResult.class));

        try {
            return call.get();
        } catch (CallNotPermittedException e) {
            // Circuit is open - payment provider is unhealthy.
            // Fail fast instead of piling up timeouts.
            return PaymentResult.temporarilyUnavailable();
        }
    }
}

Configured with Resilience4j, this circuit breaker trips (“opens”) after a threshold of failures within a rolling window, immediately rejecting further calls to the failing payment provider for a cooldown period rather than letting every checkout request pile up waiting on a doomed call. This keeps the rest of the system responsive and gives the failing dependency room to recover.

9.3 Disaster Recovery

DR.a

Multi-region deployment

Critical services run in at least two regions; DNS-based or global load balancer failover redirects traffic if an entire region becomes unavailable.

DR.b

Database backups & PITR

Continuous WAL (write-ahead log) archiving for PostgreSQL enables restoring the Order DB to any point in time, protecting against data corruption, not just hardware failure.

DR.c

Runbooks & game days

Teams regularly simulate failures (a “game day”) — killing a Redis node, a Kafka broker, or an entire region — during low-traffic periods to verify failover actually works before a real high-stakes live event depends on it.

9.4 Graceful Degradation

Not every failure needs to become a full outage. The system is designed so lower-priority features degrade first, protecting the core “watch and buy” path:

  • If the Chat Service is overloaded, chat can be temporarily throttled or paused while video and checkout continue uninterrupted.
  • If the recommendation/personalization service is down, viewers still see the default product lineup instead of a broken page.
  • If analytics ingestion (Kafka to the warehouse) lags, dashboards show slightly stale numbers, but no customer-facing functionality is affected.
💬
What an interviewer may ask

“During a huge live event, the Payment Service’s third-party provider starts timing out. What happens?” Expect a candidate to describe the circuit breaker opening, requests failing fast with a clear “try again” message instead of hanging, and possibly routing to a secondary payment provider if one is configured, rather than the whole Order Service backing up and becoming unresponsive.

10

Security

10.1 Authentication and Authorization

  • OAuth2 / OpenID Connect for viewer and creator login, issuing short-lived JWT access tokens plus longer-lived refresh tokens.
  • API Gateway as the enforcement point: every request’s JWT is validated at the gateway before it ever reaches a backend microservice, so individual services can trust the identity context passed to them rather than each re-implementing token validation.
  • Role-based access control (RBAC): distinguishes viewer, creator, moderator, and platform-admin permissions — for example, only a creator or moderator for a given stream can remove chat messages or ban a user in that room.

10.2 Payment and PCI-DSS

Raw card numbers never touch our own servers. The client-side SDK (e.g., Stripe.js/Elements) tokenizes card details directly with the payment processor in the browser or app, and our Payment Service only ever handles that opaque token, never the underlying card number. This keeps almost the entire platform out of PCI-DSS’s strictest compliance scope (SAQ A rather than the far more demanding SAQ D).

10.3 Protecting the Flash-Sale Moment from Abuse

10.3.a

Bot & scalper mitigation

Rate limiting per user/IP/device fingerprint on the “Buy” endpoint, CAPTCHA challenges triggered by suspicious velocity, and purchase-limit-per-account rules (e.g., max 2 units of a hyped item per customer) to keep drops fair for real viewers rather than automated scripts.

10.3.b

Idempotency keys

Prevent a retried or replayed request from creating a duplicate charge or double-decrementing stock.

10.3.c

DDoS protection

The CDN and edge layer absorb volumetric attacks before they ever reach origin infrastructure; the API Gateway applies stricter rate limits during detected anomalous traffic patterns.

10.4 Content and Chat Moderation

  • Automated profanity/spam/hate-speech filtering on chat messages before broadcast, backed by a moderation ML model or third-party moderation API.
  • Creator/moderator tools to mute, ban, or slow-mode chat during a stream.
  • Video content moderation (automated flagging for policy violations) for creators, especially relevant for platforms open to a wide range of independent sellers.

10.5 Data Protection

  • Encryption in transit (TLS everywhere, including between internal services in stricter deployments — mTLS) and encryption at rest for databases holding personal and payment-related data.
  • Field-level encryption or tokenization for sensitive personally identifiable information (shipping addresses, phone numbers).
  • Strict data retention and deletion policies to comply with privacy regulations (such as GDPR in Europe or India’s DPDP Act), including honoring user data-deletion requests across every service that stores their data.
Common mistake

Treating rate limiting only as a performance concern. During a live shopping drop, aggressive automated buying scripts are a real security and fairness problem, not just a load problem — the rate limiting and abuse-detection layer should be designed with that adversarial mindset from day one.

11

Monitoring, Logging and Metrics

11.1 What We Must Watch During a Live Event

MetricWhy it mattersTooling
Video startup time & buffering ratioDirectly affects whether viewers stay watching (and buying)Real-user monitoring (RUM) from the video player SDK
Checkout p50/p95/p99 latencySlow checkout during a drop causes retries, which worsens load furtherPrometheus histograms, Grafana dashboards
Inventory reservation error rateSpikes may indicate a Redis hot-key or cluster issueCustom application metrics + alerting
Order-to-payment success ratioDrops may indicate a payment provider outageBusiness metrics dashboard, alerting on threshold breach
WebSocket connection count & message fan-out latencyIndicates chat/presence system health under loadPer-instance connection metrics, Prometheus
Kafka consumer lagGrowing lag means downstream systems (shipping, notifications, analytics) are falling behindKafka’s built-in consumer group metrics, Burrow/Kafka Exporter

11.2 Distributed Tracing

A single purchase touches the Gateway, Order, Inventory, Payment, and database — tracing that whole journey with one shared trace ID is essential for debugging. Using OpenTelemetry, every service propagates trace context automatically:

OrderController.java
@RestController
public class OrderController {

    private static final Tracer tracer =
        GlobalOpenTelemetry.getTracer("order-service");

    @PostMapping("/orders")
    public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest req) {
        Span span = tracer.spanBuilder("createOrder")
            .setAttribute("product.id", req.getProductId())
            .setAttribute("user.id", req.getUserId())
            .startSpan();

        try (Scope scope = span.makeCurrent()) {
            ReservationResult reservation = inventoryClient.reserve(req);
            span.setAttribute("reservation.result", reservation.name());

            if (reservation != ReservationResult.RESERVED) {
                span.setStatus(StatusCode.ERROR, "out_of_stock");
                return ResponseEntity.status(409).body(OrderResponse.outOfStock());
            }

            PaymentResult payment = paymentClient.charge(req);
            span.setAttribute("payment.result", payment.name());
            // ... persist order, publish event ...
            return ResponseEntity.ok(OrderResponse.from(payment));
        } finally {
            span.end();
        }
    }
}

With this in place, when a customer reports “my buy button spun forever,” an engineer can pull up the exact trace for that request ID and see precisely which service or downstream call was slow — the Inventory reservation, the Payment provider, or the database write — instead of guessing.

11.3 Alerting Philosophy

  • Alert on symptoms that affect customers (checkout latency, error rate, video buffering) rather than only on low-level infrastructure metrics (CPU, memory) that don’t necessarily indicate customer impact.
  • Use multi-window, multi-burn-rate alerting (based on Google’s SRE practices) so a brief blip doesn’t page an engineer at 2 a.m., but a sustained problem during a live event pages immediately.
  • Maintain a real-time “event health” dashboard specifically for the duration of a scheduled major live shopping event, watched actively by an on-call team, since these events are pre-planned and high-stakes.
12

Deployment and Cloud

12.1 Container Orchestration

All stateless microservices (Order, Inventory, Catalog, Chat, Payment client, Notification) are packaged as containers and run on Kubernetes, which handles scheduling, health checking, and automatic restarts. Kubernetes’ Horizontal Pod Autoscaler (HPA) scales each service’s replica count based on real-time CPU, memory, or custom metrics (like queue depth or requests-per-second) — critical for handling the sudden spike when a live event begins.

order-service-hpa.yaml
# Simplified HPA config for the Order Service
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 10
  maxReplicas: 500
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "200"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 200
          periodSeconds: 30

Note the aggressive scale-up policy (doubling capacity every 30 seconds with no stabilization delay) — appropriate here because live shopping traffic spikes are sudden and predictable in timing (the event has a scheduled start), so we would rather over-provision briefly than fall behind demand in the first critical seconds.

12.2 Pre-Warming for Scheduled Events

Because major live shopping events are scheduled in advance (unlike organic traffic spikes), the platform can proactively scale up capacity minutes before the event starts, rather than relying solely on reactive autoscaling that might not react fast enough for the first wave of a synchronized spike. This is a significant operational advantage live shopping has over unpredictable viral traffic.

12.3 Multi-Region Cloud Topology

  • Video ingest and CDN: distributed globally by design (this is standard practice for any major cloud/CDN provider).
  • Commerce services: deployed in multiple regions, with the Order DB primary in one region and read replicas in others, routing writes to the primary region and reads to the nearest replica.
  • Data residency: for platforms operating across regulatory boundaries (e.g., needing to keep Indian users’ data within India under local data protection rules), region-pinned deployments and data stores keep specific customer data within required jurisdictions.

12.4 Progressive Delivery

New versions of services are rolled out using canary deployments — a new version first receives a small percentage (say 5%) of production traffic, monitored closely for error-rate or latency regressions, before gradually increasing to 100%, with automatic rollback if the canary’s metrics degrade. This is especially important for the Inventory and Order services, where a subtle bug could cause real financial and customer-trust damage if rolled out to 100% of traffic immediately — and deployments are typically frozen entirely during scheduled major live events as an extra safety measure.

13

Databases, Caching and Load Balancing

13.1 Choosing the Right Database for Each Job

DataStoreWhy
Orders, payments, inventory ledgerSharded PostgreSQL (relational)Needs ACID transactions, strong consistency, and well-understood locking behavior for money and stock
Product catalog (title, description, images, variants)Document store (MongoDB)Flexible schema across many product types/categories, read-heavy, tolerant of eventual consistency
Product/creator search & discoveryElasticsearchFull-text search, faceted filtering (price range, category, in-stock only) at low latency
Hot inventory counters, sessions, product cacheRedis (in-memory)Sub-millisecond atomic operations, essential for the flash-sale checkout path
Analytics & historical reportingCloud data warehouse (BigQuery/Snowflake)Optimized for large-scale aggregation queries over historical event data, decoupled from live traffic

13.2 Sharding the Order Database

The Order DB is sharded by a key such as user_id or region, so that no single database instance needs to handle the entire platform’s order volume. Each shard is a fully independent PostgreSQL primary/replica pair. A lightweight routing layer in the Order Service determines which shard owns a given order based on the shard key, consistent with typical horizontal partitioning strategy used across large-scale e-commerce systems.

13.3 Caching Strategy

  • Cache-aside for product data: the Catalog Service checks Redis first; on a miss, it reads from MongoDB and populates the cache, with a time-to-live (TTL) to bound staleness.
  • Write-through for inventory: unlike product data, inventory writes go to Redis first (for the fast atomic check), then asynchronously reconciled to the database, as described in Section 5.3 — a deliberate exception to the usual cache-aside pattern because raw speed on the write path is the priority here.
  • Cache invalidation on product update: when a creator edits a product’s price or description mid-stream, the Catalog Service publishes an invalidation event so all cache nodes drop the stale entry rather than waiting out the TTL.
Analogy

Cache-aside is like checking a sticky note on your fridge before walking to the store — if the note (cache) is missing or old, you go to the store (database) and update the note. Write-through for inventory is different: you update the sticky note the instant something changes, and only later walk to the store to make sure its records agree, because you need everyone reading the note right now to see the truth immediately.

13.4 Load Balancing Deep Dive

Load balancer typeWhere usedWhy
Layer 4 (transport-level, e.g. AWS NLB)In front of the Redis cluster and Kafka brokers, and for raw WebSocket/RTMP connectionsVery low overhead, connection-level routing, ideal for persistent connections and non-HTTP protocols
Layer 7 (application-level, e.g. Envoy/NGINX)In front of the API Gateway and HTTP microservicesCan route based on URL path, headers, and cookies; supports retries, circuit breaking, and TLS termination
Global/DNS-based load balancingRouting users to the nearest healthy regionDirects traffic to the closest, healthiest data center, reducing latency and enabling regional failover

13.5 Consistent Hashing for Cache and Chat Sharding

Both the Redis cluster (for inventory keys) and the Chat Service’s room assignment rely on consistent hashing to distribute keys/rooms across nodes. Consistent hashing ensures that when a node is added or removed (scaling up for an event, or recovering from a failure), only a small fraction of keys need to move to a new node, rather than reshuffling almost everything — which would happen with naive modulo-based hashing.

14

APIs and Microservices

14.1 Choosing Protocols per Use Case

InteractionProtocolWhy
Client to API Gateway (buy, browse)REST over HTTPSSimple, cacheable, universally supported by web and mobile clients
Client chat/reactions/live stock updatesWebSocketPersistent bidirectional connection needed for real-time push
Service-to-service calls (Order to Inventory, Order to Payment)gRPCLow-latency binary protocol, strongly typed contracts, built-in streaming support — well suited to internal high-throughput calls
Asynchronous cross-service eventsKafka (event-driven, not request/response)Decouples producers and consumers; supports replay and multiple independent subscribers

14.2 Example REST Contract: Reserve and Purchase

buy.http
POST /api/v1/orders
Headers: Authorization: Bearer <jwt>, Idempotency-Key: <uuid>
Body:
{
  "productId": "prod_98213",
  "streamId": "stream_44210",
  "quantity": 1,
  "paymentMethodToken": "tok_abc123"
}

Response 200 OK:
{
  "orderId": "ord_77123",
  "status": "PAID",
  "amount": 2499,
  "currency": "INR"
}

Response 409 Conflict:
{
  "error": "OUT_OF_STOCK",
  "message": "This item just sold out."
}

14.3 Example gRPC Contract: Inventory Service

inventory.proto
syntax = "proto3";

service InventoryService {
  rpc Reserve (ReserveRequest) returns (ReserveResponse);
  rpc Release (ReleaseRequest) returns (ReleaseResponse);
  rpc GetStock (StockRequest) returns (stream StockUpdate); // server-streaming for live counts
}

message ReserveRequest {
  string product_id = 1;
  string order_id = 2;
}

message ReserveResponse {
  bool reserved = 1;
  int32 remaining_stock = 2;
}

The GetStock RPC uses gRPC’s server-streaming capability — a single call keeps a stream open and the server pushes new stock values as they change, which is how internal dashboard services and the Presence Service can get live inventory updates without polling.

14.4 Microservice Boundaries: Why Split It This Way?

Service boundaries in this design follow the principle of splitting along independent scaling needs and independent rates of change, not just “one service per database table.” Catalog changes rarely and is read-heavy; Inventory changes constantly and must be extremely fast and safe; Payment has unique compliance requirements; Chat has entirely different connection-lifecycle characteristics (long-lived WebSocket state) than the mostly-stateless REST services. Each boundary reflects a genuinely different operational profile, which is the right reason to draw a service line — splitting services just for the sake of “microservices” without a clear independent-scaling or independent-ownership reason tends to just add complexity without benefit.

💬
What an interviewer may ask

“Would you combine the Order Service and Inventory Service into one service, since they’re so tightly coupled in the purchase flow?” A thoughtful answer acknowledges this is a legitimate design choice with real trade-offs: combining them reduces network hops and simplifies the transaction, but couples their deployment and scaling together, and blurs the safety-critical inventory logic with the broader, faster-changing order orchestration logic. Many real systems do keep them separate specifically to isolate inventory’s correctness-critical code from the rest of order processing, but it is a genuine trade-off, not a rule.

15

Design Patterns and Anti-Patterns

15.1 The Saga Pattern (Distributed Transactions)

A purchase spans three services (Inventory, Payment, Order) with no shared database transaction across them. The Saga pattern handles this by breaking the operation into a sequence of local transactions, each with a defined compensating action if a later step fails.

Reserve Inventorylocal tx Charge Paymentlocal tx Confirm Orderlocal tx Compensate: Release Inventory success success payment failure downstream failure
Fig 15.1 — Orchestrated saga: three local transactions in sequence, with a compensating action releasing the inventory hold if any downstream step fails.

We use an orchestration-based saga, where the Order Service explicitly plays the role of orchestrator, calling each step in order and triggering compensations on failure — this is easier to reason about and debug than a choreography-based saga (where each service reacts to the previous service’s event with no central coordinator), which tends to work better for longer, more loosely-coupled workflows like post-payment fulfillment.

15.2 CQRS (Command Query Responsibility Segregation)

The Inventory Service’s write path (atomic Redis decrement) and read path (serving “N left” to millions of viewers) have very different requirements — writes need strict correctness and low contention, reads need to scale to enormous fan-out. CQRS separates these: writes go through the tightly controlled reservation path, while the “current stock” number shown to viewers is served from a separately scaled, cached read model that is updated via the Kafka InventoryUpdated event stream, rather than every viewer’s screen hitting the authoritative counter directly.

15.3 Event Sourcing for Inventory Auditability

Rather than only storing the current stock count, the Inventory Service also appends every reservation, confirmation, and release as an immutable event to Kafka. This gives a complete, replayable audit trail — essential for resolving disputes (“why does the count not match what I expected?”) and for reconstructing state if a bug ever requires rebuilding the cache from scratch.

15.4 Anti-Patterns to Avoid

Anti-pattern: Synchronous chain of calls on the critical path

If Order Service synchronously calls Inventory, which synchronously calls a Notification Service, which synchronously calls an Analytics Service, before returning success to the user, then the slowest link in that entire chain determines checkout latency, and any one failure blocks the purchase entirely. Non-essential steps (notifications, analytics) belong on the asynchronous event bus, not the synchronous checkout path.

Anti-pattern: No idempotency on write endpoints

Mobile networks are unreliable; a client may retry a “Buy” request that actually succeeded server-side but timed out on the response. Without an idempotency key, this creates duplicate orders and double-charges. Every mutating endpoint on the critical path must be idempotent.

Anti-pattern: Treating the cache as optional

Some teams build the “correct” database-only path first and bolt on caching later purely as a performance afterthought. For inventory specifically, the cache (Redis atomic operations) is not just a performance optimization — it is the primary correctness mechanism under load. Designing it in from the start avoids a painful late rearchitecture.

Anti-pattern: Shared mutable database across services

If the Chat Service and the Order Service both read and write the same database tables directly, a schema change for one team’s needs can silently break the other team’s service. Each service should own its data and expose it only through its API or published events.

💡
Good pattern: Circuit breaker + bulkhead isolation

Combining circuit breakers (Section 9.2) with bulkheads — dedicating separate, isolated thread/connection pools per downstream dependency — ensures that a slow Payment provider can’t exhaust the thread pool that the Order Service also needs for calling Inventory, keeping unrelated functionality healthy even while one dependency struggles.

16

Best Practices and Common Mistakes

16.1 Best Practices

BP 1

Design for the spike, not the average

Provision and load-test for the peak-second demand of a hyped drop, not the average traffic across the whole stream — average numbers hide the exact moment that actually breaks systems.

BP 2

Make the “Buy” button give instant feedback

Even before the server responds, the UI should visually acknowledge the tap immediately (a loading state), reducing the temptation for users to tap repeatedly, which would otherwise multiply load.

BP 3

Separate correctness-critical paths

Inventory and payment logic deserve the most rigorous testing, code review, and monitoring in the entire system, precisely because mistakes there have direct financial and trust consequences.

BP 4

Load test with realistic, synchronized patterns

A gradual ramp-up load test does not reveal how the system behaves under a genuine thundering-herd spike; load tests should specifically simulate thousands of simultaneous requests hitting the same product at the same instant.

BP 5

Pre-warm caches and infrastructure

Since major live shopping events have known start times, proactive scaling avoids relying entirely on reactive autoscaling during the first, most dangerous seconds of a spike.

BP 6

Build a “kill switch” for features under stress

Operators should be able to quickly disable non-essential features (e.g., certain chat effects, recommendations) during an ongoing incident, to shed load from the parts of the system that matter least.

16.2 Common Mistakes

Using a single global lock for inventory across all products

This serializes unrelated purchases of different products through one bottleneck; locking should always be scoped to the individual product (or even sub-shards of a single hot product), never global.

Underestimating chat fan-out costs

Naively looping through a list of connected users’ sockets in application code to broadcast a chat message does not scale; a proper pub/sub fan-out mechanism is required from the start.

Forgetting to release reservations on client abandonment

If a viewer starts checkout but closes the app before completing payment, without a TTL-based hold expiry (Section 5.2), that unit could be locked away from other buyers indefinitely.

Ignoring video-checkout latency mismatch

If video is delayed by 8 seconds but chat/stock updates are near-instant, viewers may see chat reacting to a moment they haven’t watched yet, or see “sold out” before the creator even finishes describing the item on their delayed video feed — confusing UX that erodes trust. Keeping video and data-layer latencies reasonably aligned (or explicitly designing around the gap) matters.

Not testing failure/rollback paths as thoroughly as the happy path

Teams often stress-test successful purchases extensively but under-test what happens when payment fails after inventory reservation — exactly the path most likely to cause subtle inventory drift bugs in production.

17

Real-World Industry Examples

Live shopping is not a hypothetical exercise — it is a large and fast-growing part of e-commerce today. A few real platforms illustrate different points on the design spectrum we’ve discussed:

TikTok

TikTok Shop

Built directly into TikTok’s short-video and live-streaming app, TikTok Shop turns the existing massive live audience into a real-time sales floor, blending the video plane and commerce plane into a single native experience rather than linking out to a separate storefront. It has become one of the primary drivers of live shopping growth in the US market since its 2023 launch, with reported year-over-year sales growth in some categories exceeding 100%.

Amazon

Amazon Live

Amazon Live lets Amazon sellers and approved influencers broadcast product demonstrations directly within Amazon’s existing marketplace and checkout infrastructure. Because it reuses Amazon’s already-proven catalog, inventory, and payment systems rather than building commerce from scratch, its architecture emphasizes tight integration between the video layer and Amazon’s existing order pipeline over building an independent commerce stack. Industry reporting suggests the large majority of Amazon Live viewers take some action — clicking through or buying — during or immediately after a stream, underscoring how directly video engagement here converts into checkout load.

Whatnot

Live-auction native

Whatnot built its entire platform around live-auction-style selling for collectibles, trading cards, sneakers, and fashion — a format where “inventory” is often literally a single unique item, making the atomic reservation problem from Section 5 especially visible: exactly one bidder can win each item, live, on camera, with everyone watching the countdown at once.

Alibaba

Taobao Live

Taobao Live pioneered live commerce at massive scale in China years before most western platforms, with top streamers selling volumes of merchandise in a single broadcast that rival the revenue of entire retail chains over much longer periods. Its scale requirements are what pushed the industry toward exactly the kind of highly-sharded, cache-first inventory architecture described in this tutorial.

Enterprise SaaS

Brand-owned live commerce

Dedicated live-commerce SaaS platforms have also matured to serve brands directly on their own websites rather than through a third-party marketplace, prioritizing tight, low-friction integration between the video experience and existing e-commerce backends such as Shopify, so that catalog and inventory data sync natively rather than through custom integration work.

Takeaway

Every one of these platforms, despite different business models, converges on the same core technical shape — a fast video pipeline, an aggressively cached and atomically-safe inventory layer, and a real-time social layer — because the underlying constraints (compressed demand, synchronized viewers, zero-tolerance for overselling) are the same no matter who builds the platform.

18

FAQ

Q1

What happens if two viewers click “Buy” on the very last unit at the exact same millisecond?

Redis processes commands against a single key one at a time, even under massive concurrent load, because the atomic Lua script (Section 5.2) executes as one indivisible unit. Whichever request’s script execution happens to run first (a matter of nanoseconds, decided by the server, not by the client) succeeds; the other receives an immediate, honest “out of stock” response. There is no window where both could succeed.

Q2

Why not just use a database transaction with row-level locking instead of Redis?

A relational database can absolutely enforce this correctness with row-level locking (e.g., SELECT ... FOR UPDATE), but under extreme concurrency — thousands of simultaneous requests targeting the same row — lock contention and connection pool exhaustion become serious bottlenecks. Redis’s in-memory, single-threaded command execution handles this specific pattern (high-frequency atomic counter operations) with far higher throughput, which is why it’s used as the fast path, with the database serving as the durable backstop.

Q3

How do you prevent a bug or crash from leaving the Redis stock count permanently out of sync with the database?

A periodic reconciliation job compares Redis counters against the database’s authoritative ledger for recently active products, alerting or auto-correcting on drift. Because every reservation, confirmation, and release is also logged as an event (Section 15.3), any discrepancy can be traced back to its root cause using the event history, not just patched blindly.

Q4

How does the system handle a creator’s internet connection dropping mid-stream?

The Stream Ingest Service detects the dropped connection quickly via heartbeat timeouts. The player shows a “reconnecting” state to viewers rather than abruptly cutting the stream, and if the creator’s app is configured with a backup ingest endpoint or automatically retries, the stream resumes with minimal disruption. Any products that were actively selling remain purchasable in a paused state; the platform typically does not auto-cancel in-progress orders just because the video feed briefly drops.

Q5

Can viewers still buy a product after the live stream ends?

Yes — most platforms keep the stream’s replay (VOD) available afterward with the same shoppable product tags, letting viewers who missed the live moment still purchase, as long as stock remains, through the same Catalog and Inventory services, just without the live urgency and chat layer active.

Q6

How would you extend this design to support live auctions instead of fixed-price “buy now” purchases?

An auction adds a bidding sub-flow: instead of an immediate atomic decrement, each bid is validated against the current highest bid (again using an atomic Redis operation to avoid race conditions between simultaneous bids) and broadcast to viewers via the same chat/presence pub-sub mechanism. Only when the countdown ends does the Order Service create an order for the winning bidder, reusing the exact same reservation-then-confirm flow described in Section 5.

19

Summary and Key Takeaways

Designing a live shopping platform means designing three systems that must feel like one seamless experience to the viewer: a low-latency video pipeline, a rock-solid real-time inventory and checkout system, and a highly scalable social/chat layer. The single hardest and most important problem is preventing overselling under extreme, synchronized concurrency, solved here with atomic operations in an in-memory cache, backed by durable, optimistically-locked persistence in a relational database as a safety net.

The six ideas that hold this whole system together

  • Not every part of the system needs the same consistency guarantee. Money and stock demand strong consistency; viewer counts and chat can be eventually consistent and approximate.
  • Atomic operations, not application-level check-then-write, are what actually prevent race conditions under real concurrent load.
  • Event-driven design (Kafka) decouples the fast, critical checkout path from slower, non-essential downstream work like shipping, notifications, and analytics.
  • A CDN is non-negotiable for video at this scale — direct server-to-viewer delivery cannot support millions of simultaneous watchers.
  • Because live events are scheduled, not organic, proactive pre-scaling beats purely reactive autoscaling for surviving the first critical seconds of a spike.
  • Graceful degradation matters: a well-designed system keeps the core “watch and buy” experience alive even when secondary features (chat, recommendations, analytics) are under stress.
Closing thought

None of the individual pieces here are exotic — CDNs, Kafka, Redis, Kubernetes, and relational databases are all well-understood, widely used technologies. What makes live shopping a genuinely hard system design problem is the combination: video, commerce, and social interaction all converging on the same short, synchronized, high-stakes window of time, with real money and real inventory on the line, watched live by everyone at once.