Skip to content
Abhijeet Maharana, home
← All case studies

Independent build · Lead Systems Architect & Full-Stack Engineer

AI-Powered Real-Time Commerce Engine

Case study by Abhijeet Maharana

TL;DR: I designed and built a real-time ordering and inventory platform end to end. Customers and fulfilment staff see order status live over SSE and WebSockets, Redis locks plus PostgreSQL transactions make overselling impossible, and AI semantic search lets people find items by intent instead of exact names.

What problem did this system need to solve?

Ordering systems break in three predictable places when traffic arrives in bursts:

  • Status is stale. Customers refresh a page to learn whether their order was accepted, and staff screens poll a database that is already behind.
  • Stock goes negative. Two customers check out the last unit at the same moment, both succeed, and someone has to apologise later.
  • Search is literal. People type what they want ("something light and vegetarian"), and keyword search returns nothing.

The goal was a platform that stays correct under concurrent load and feels live on every screen.

What did I build?

I owned the architecture and wrote the full stack:

  • Customer ordering app in Next.js and TypeScript, with a fast, mobile-first UI that tolerates flaky connections.
  • Order and inventory services in Node.js on PostgreSQL, with Redis for locks and fast shared state.
  • Live order tracking for customers, and a real-time fulfilment display for the staff who prepare and hand over orders.
  • AI semantic search and recommendations over the catalogue.

How does live order tracking work?

Each order moves through a small state machine (placed, accepted, preparing, ready, completed). Every transition is written to PostgreSQL first, then published as an event to the screens that care about that order.

  • Customers get Server-Sent Events (SSE). The traffic is one-way (server to browser), SSE runs over plain HTTP, passes through proxies cleanly, and the browser reconnects on its own. Sending a Last-Event-ID on reconnect means a customer who drops off for a few seconds still receives the updates they missed.
  • Staff screens use WebSockets. Fulfilment staff push changes back ("accepted", "ready"), so a two-way channel fits better there.

Picking the transport per screen, instead of WebSockets everywhere, kept the customer side simpler and cheaper to run.

How do you stop two customers buying the last item?

Two layers, so that either one alone would keep stock correct:

  1. A short Redis lock per item (SET key token NX PX ttl) serialises checkouts for hot items across all server instances. Only the holder of the token can release it.
  2. A conditional update inside a PostgreSQL transaction is the final guard. The decrement only succeeds if enough stock is left, so even an expired lock cannot push stock below zero.
// Simplified: reserve stock for one line item.
const token = crypto.randomUUID();
const locked = await redis.set(`lock:sku:${sku}`, token, { NX: true, PX: 3000 });
if (!locked) throw new RetryableError("Item is busy, retrying");

try {
  await db.transaction(async (tx) => {
    const { rowCount } = await tx.query(
      "UPDATE inventory SET qty = qty - $1 WHERE sku = $2 AND qty >= $1",
      [quantity, sku],
    );
    if (rowCount === 0) throw new SoldOutError(sku);
    await tx.query("INSERT INTO order_items (order_id, sku, qty) VALUES ($1, $2, $3)", [
      orderId,
      sku,
      quantity,
    ]);
  });
} finally {
  await releaseLock(`lock:sku:${sku}`, token); // compare-and-delete, so we never free someone else's lock
}

How does the AI search work?

Catalogue items are converted into vector embeddings when they are created or edited. A search query is embedded the same way, and the closest items by vector similarity come back first, so "something light and vegetarian" finds salads and soups that never use those words. The same similarity signal drives "you might also like" recommendations.

What were the results?

AreaOutcome
Order statusLive on every customer and staff screen, no manual refresh
InventoryStock cannot go negative, even under concurrent checkouts or an expired lock
SearchIntent-based results and recommendations instead of exact keyword matches
FrontendMobile-first UI built to handle unreliable connections

What would I do the same way next time?

Choosing the real-time transport per audience (SSE for read-only customers, WebSockets for staff who write back) and treating the database constraint as the real source of truth, with the Redis lock as a performance layer on top. Both decisions kept the system simple to reason about when things went wrong.