At 9:00 the sale starts. The CDN keeps product pages up, but checkout fails: two shoppers buy the last unit of the same jacket, a retry after a timeout leaves two payment holds on one card, and the warehouse hears about an order an hour late because an event got lost.
None of these is a capacity problem. They are boundary problems: who owns which data, and which steps must finish while the shopper waits.
This post walks through a realistic e-commerce architecture for a mid-size store: components and data ownership, the checkout path step by step, the order pipeline after it, peak traffic, and a diagram of the platform.
Start from what shoppers do
Five journeys cover most of what a store does, with very different demands:
| Journey | Components on the path | What may be stale |
|---|---|---|
| Browse | CDN, gateway, catalog | Prices and stock on a cached page, for minutes |
| Search | Search index | Changed products, for seconds |
| Cart | Cart | Nothing the shopper just changed |
| Checkout | Checkout, inventory, payment provider, orders | Nothing |
| Track order | Orders, fulfillment | Shipping status, for minutes |
Browse and search carry most requests and tolerate staleness; checkout carries few and tolerates none. Most of the architecture follows from that split.
The components and the data each one owns
At the edge, a CDN caches pages and images, and an API gateway (or one backend for frontend per client type) authenticates, rate-limits and routes the rest. Each service behind them owns its data:
| Service | Owns | Typical store | Consistency need |
|---|---|---|---|
| Catalog | Products, variants, prices | Postgres, read through caches | Eventual; checkout re-reads prices |
| Search | Product index with facets | OpenSearch or Elasticsearch | Eventual, updated from catalog changes |
| Cart | Cart lines per shopper | Redis with a TTL | Read-your-writes per shopper |
| Checkout | Sessions: totals, reservation and payment IDs | Postgres | Strong within a session |
| Inventory | Stock per SKU, reservations | Postgres | Strong: never oversell |
| Orders | Orders, state history, outbox | Postgres | Strong: one writer per order |
| Payment service provider (PSP) | Authorizations, captures, refunds | The PSP | The PSP's record wins |
| Fulfillment | Shipments, tracking numbers | WMS or 3PL | Eventual, driven by events |
Other services use the owner's API or events, never its tables. Stock and orders need transactions; carts and the search index can lag, or even lose a write, without real harm.
The checkout path, step by step
Checkout is the one flow where the shopper waits on several services at once:
Re-price the cart. Checkout reads the cart, fetches current prices from the catalog and computes tax, shipping and discounts server-side. Never trust a browser's total.
Reserve stock with an expiry, here 15 minutes:
-- One transaction: take the stock, then record the hold UPDATE stock SET reserved = reserved + 2 WHERE sku = 'JKT-042-M' AND on_hand - reserved >= 2; -- 0 rows: out of stock, stop INSERT INTO reservations (checkout_id, sku, qty, expires_at) VALUES ('chk_41a', 'JKT-042-M', 2, now() + interval '15 minutes');When two shoppers race for the last jacket, PostgreSQL's default Read Committed level makes the second update wait and re-check its WHERE clause against the new row, so one gets zero rows. A sweeper returns expired holds to stock. On a hosted payment page, align the clocks: Stripe Checkout sessions last 24 hours unless you set a shorter expiry (30 minutes minimum) or expire them yourself;
checkout.session.expiredtells you to release the items.Create the payment with an idempotency key. Checkout asks the PSP for a payment of the server-side total, with manual capture and an idempotency key stored before the call. After a timeout, retry with the same key: Stripe returns the saved response instead of creating a second payment. Its keys are up to 255 characters and may be pruned after 24 hours.
Authorize. Extend the stock hold first, so it cannot lapse mid-payment. The shopper types card details into the PSP's fields, so card numbers never reach your servers, and the issuer may ask for 3-D Secure. Success puts the funds on hold: a Stripe payment with manual capture moves to
requires_captureand emitspayment_intent.amount_capturable_updated. Trust that webhook, not the browser's redirect; see the payment gateway integration diagram.Place the order. Orders creates the order as
placedand writesOrderPlacedto an outbox table in the same transaction. Checkout then tells inventory to turn the reservation into an allocation.Capture on shipment. When fulfillment ships, Orders captures. At the time of writing (November 2026), Stripe documents that online card authorizations usually last 7 days, or 5 days (exactly 4 days and 18 hours) for Visa merchant-initiated transactions; then the funds are released and the payment is canceled. Most card payments allow one capture, and a partial capture releases the rest.
If a step fails before the order exists, undo the earlier ones in reverse: cancel the authorization, release the hold. That is a saga with checkout as its orchestrator.
Where money, stock and order state change
| Order state | Set when | Payment | Stock |
|---|---|---|---|
| Checkout open | Stock is reserved | Created, not authorized | Held for 15 minutes |
| Placed | PSP reports the authorization | Funds on hold | Allocated to the order |
| Shipped | Fulfillment ships | Captured | Deducted from on-hand |
| Canceled | Canceled before shipping | Authorization canceled | Released |
| Returned | Return received | Refunded | Restocked after inspection |
The hard cases sit between the rows. If an authorization arrives after the hold expired, re-reserve if stock remains; otherwise cancel the authorization and tell the shopper. If a backorder outlasts the 7-day window, capture the full amount at the first shipment, use an extended authorization where your PSP offers one (up to 30 days on some card networks, according to Stripe), or charge upfront and refund what you cannot ship. Some payment methods, such as ACH and iDEAL, cannot capture separately at all.
After the order is placed
Everything after placed runs asynchronously. A relay publishes OrderPlaced from the outbox to the order-events topic, so no event is lost between commit and broker (see the transactional outbox pattern). Fulfillment picks, packs and ships, then publishes OrderShipped, which triggers the capture. Notifications sends emails; analytics loads every event into the warehouse.
Delivery is at least once, so consumers deduplicate by event ID. Keying the topic by order ID keeps each order's events in sequence.
The microservices architecture diagram charges the card after OrderPlaced instead. That fits payments without shopper interaction; 3-D Secure needs the shopper present, so this design authorizes inside checkout.
Surviving peak traffic
Assume a sale hour with 7,200 orders, a 2% conversion rate and 10 page views per session: 360,000 sessions and 3.6 million page views, or about 1,000 page views per second against 2 orders per second. Each half needs its own protection:
- Cache the catalog at the edge. With
Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=60, the CDN serves a product page for five minutes, then up to a minute longer while it refetches in the background, as RFC 5861 defines. At a 90% hit rate the origin sees 100 page views per second instead of 1,000. Purge on price changes; checkout re-checks anyway. - Queue in front of checkout. A virtual waiting room admits shoppers at a rate inventory and the PSP can sustain, as in the ticket booking system design.
- Protect hot SKUs. Reservations for one SKU update one row, one at a time. At 2 ms of lock time per transaction, that row tops out near 500 reservations per second. Keep the transaction short and free of PSP calls, split a doorbuster's stock across several rows, and cap the quantity per order.
- Know your PSP's rate limits before the sale.
Start smaller than this
Many stores should not build all of this. A hosted platform such as Shopify or BigCommerce runs catalog, cart and checkout for you; your diagram then shows the apps and webhooks around it.
A custom build often starts best as a modular monolith: the same boundaries as modules in one deployable. In 2019 Shopify's engineers described reorganizing one of the largest Ruby on Rails codebases around orders, shipping, inventory and billing instead of splitting it into microservices. Extract a service when a module needs its own scaling or releases; the diagram still applies.
The e-commerce platform diagram
Diagram contents
- Shopper
- CDN
- API gateway / BFF
- Catalog
- Search
- Cart
- Checkout
- Orders
- Inventory
- Payment provider (PSP)
- order-events
- Fulfillment
- Notifications
- Analytics warehouse
- HTTPS
- misses, API
- GET /products
- GET /search
- POST /cart/items
- POST /checkout
- GET /orders/{id}
- product changes
- read cart
- reserve, 15 min
- authorize + idempotency key
- place order
- capture on OrderShipped
- OrderPlaced
- placed, shipped
- all events
How to read it:
- Requests enter at the top left: shopper, CDN, then the gateway, which routes each journey to one service.
- Blue is the browse path. The dashed edge is the search index catching up with product changes.
- Amber is the checkout path, where the shopper waits: read the cart, reserve stock, authorize and place the order.
- The payment provider gets two calls: authorize during checkout, and capture on
OrderShipped(dashed teal). - Violet dashed edges are events, from the outbox through order-events to fulfillment, notifications and analytics.
- Each service names its store, and the Markdown block lists the numbers.
Drawing e-commerce architecture diagrams clearly
- Label gateway edges with routes such as
GET /products, so readers can map pages to services. - Name the store on every service. A shared database should be impossible to miss.
- Solid for waiting, dashed for later. Calls the shopper waits on should look different from events.
- Write timings on the edges. The hold duration and the capture trigger need review most.
- Draw authorization and capture as two edges. One arrow to the PSP hides the key timing.
- Leave out pods, regions, tax and fraud vendors, and the back office.
Build it in Archboard
- Sign in with Google or GitHub and create a diagram.
- Add a Person from the C4 Model pack, then a CDN and an API Gateway from the Cloud pack below it.
- Add five Container nodes from the C4 Model pack in a column: Catalog, Search, Cart, Checkout and Orders, each with its store in the description.
- Add a Container for Inventory, an External System for the payment provider and a Topic from the Data pack for order-events. To their right, add Container nodes for Fulfillment and Notifications and a Data Warehouse from the Data pack.
- Connect the gateway to the five services with route labels. Make the checkout calls solid, and the product changes, the capture and the events dashed.
- Add a Markdown block from the Core pack with the numbers, and Text notes for the legend and key rules.
If your services run from a Compose file, the Docker Compose to diagram tool drafts the boxes; the microservices architecture template is another starting point. The example uses 15 diagram nodes, within the Free plan.
FAQ
What are the main components of an e-commerce architecture? A CDN, an API gateway, services for catalog, search, cart, checkout, inventory and orders, a payment provider, and fulfillment, notifications and analytics. Hosted platforms bundle most of them.
Should an online store use microservices? Not by default. A modular monolith with the same boundaries is simpler to run, and Shopify chose one for its core. Split out a service when a module needs independent scaling or releases.
How do you prevent overselling inventory? Reserve stock with one atomic, conditional update per SKU, give each reservation an expiry, and allocate it only when the order is placed. Never decide availability from a cached page.
When should an online store capture the payment? For physical goods, usually at shipment, so you charge only for what ships. Online card authorizations usually last 7 days. On Stripe, Klarna allows 28 days, and PayPal 10, which Stripe tries to extend to 20.
How do e-commerce sites handle traffic spikes? They serve the catalog from the CDN, queue shoppers in front of checkout, and protect hot SKUs and the payment provider. Plan checkout capacity separately from page views.