E-Commerce Platform
Storefront and checkout reference architecture: React storefront, API Gateway, microservices for catalog, cart, orders, payments (Stripe), and inventory, with S
Architecture
- Shopper — End user browsing the storefront on web
- React Storefront — SPA storefront: product browsing, cart, checkout with Stripe Elements. Served via CloudFront/S3.
- Cognito User Pool — Shopper sign-up/sign-in. Issues JWTs consumed by the API Gateway authorizer.
- API Gateway — REST edge for all domain services. Cognito JWT authorizer on every route except /webhooks/stripe.
- Catalog Service — Product listing, detail, and admin CRUD. Read-heavy; candidate for caching.
- Cart Service — One active cart per user. Add/update/remove items, recalculates subtotal on every write.
- Orders Service — Validates cart + payment intent, reserves stock, persists the order, and publishes OrderPlaced to SQS.
- Payments Service — Creates Stripe PaymentIntents and handles Stripe webhooks (signature-verified).
- Inventory Service — Stock levels and reservations. Admin-only API surface; also called by the order processor.
- Products Table — DynamoDB: products keyed by product_id, GSI on category.
- Carts Table — DynamoDB: carts + cart_items, keyed by user_id. TTL abandons stale carts.
- Orders Table — DynamoDB: orders + order_items + payments, GSI on user_id for order history.
- Stripe — External PSP. PaymentIntents created server-side, confirmed client-side, settled via webhook.
- Inventory Table — DynamoDB: available/reserved quantities per product, updated with conditional writes.
- Order Processing Queue — SQS (FIFO by order_id) decoupling checkout from fulfilment. DLQ after 3 receives.
- Order Processor — Consumes OrderPlaced: confirms payment, decrements stock, flips order to confirmed, triggers email.
- SES — Transactional email: order confirmation and shipping updates from templated sends.
Workflow
- Checkout initiated — Shopper clicks Pay on the storefront
- Validate cart — Re-price items, drop inactive products, verify cart status=active
- Reserve inventory — Conditional write: quantity_available >= requested for every line item
- Stock available?
- Insufficient stock — Return 409 to client with the offending line items
- Create Stripe PaymentIntent — Amount = subtotal + shipping + tax; idempotency key = cartId
- Await payment confirmation — Client confirms card via Stripe Elements; webhook settles the intent
- Payment succeeded?
- Release reserved inventory — Move quantity_reserved back to quantity_available
- Payment failed — Order marked cancelled; shopper prompted to retry with another card
- Persist order (paid) — Write order + order_items + payment record; mark cart checked_out
- Publish OrderPlaced to SQS — FIFO by order_id; order processor takes it from here
- Send confirmation email — SES templated send from the order processor
- Order confirmed
- Checkout failed
Event flow
- Orders Service — Publishes order.placed at the end of checkout
- Stripe Webhook Handler — Verifies Stripe signatures and republishes payment outcomes internally
- order-processing (SQS FIFO) — FIFO by order_id, DLQ after 3 receives
- payment-events (SNS) — Payment outcomes, fanned out to interested consumers
- Order Processor — Joins order.placed with payment outcome; decrements stock or releases reservation
- Order Processor — Emits order.confirmed once payment and stock are settled
- order-notifications (SNS) — Downstream fan-out point; add fulfilment here
- Email Notifier (SES) — Sends templated confirmation email
- Analytics Sink (Firehose to S3) — Order events archived for BI/Athena
- order.placed — Emitted by the orders service once stock is reserved and the order row is persisted as pending/paid.
- payment.succeeded — Translated from Stripe's payment_intent.succeeded webhook after signature verification.
- payment.failed — Translated from Stripe's payment_intent.payment_failed webhook; triggers inventory release.
- order.confirmed — Emitted by the order processor after payment is verified and stock decremented. Fan-out to email and analytics.
About this design
- ## What this is A production-shaped reference architecture for a small-to-mid e-commerce platform on AWS serverless. A React storefront (Cognito-authenticated) talks to API Gateway, which fronts five Lambda-backed domain services — catalog, cart, orders, payments, and inventory — each owning its own DynamoDB table set. Checkout is decoupled from fulfilment with an SQS queue, Stripe handles card processing, and SES sends transactional email. Every view in this project describes the same system: the architecture is the component map, the database schema is the per-domain table design, the API contract is what the storefront calls, the workflow is the checkout state machine, and the event flow is the order lifecycle after checkout. ## How it works Browsing and cart are synchronous: the storefront hits `/products` and `/cart`, and each service reads/writes only its own table (prices are snapshotted onto cart items at add-to-cart time). Checkout is a three-step handshake designed so a customer is never charged without an order existing: 1. `POST /payments/intent` — the payments service creates a Stripe PaymentIntent for the cart total and returns the client secret. 2. The storefront confirms the card with Stripe Elements (card data never touches our backend). 3. `POST /orders` — the orders service re-validates the cart, reserves stock with conditional writes on the inventory table, persists the order as `pending`, and publishes `OrderPlaced` to SQS. The order processor consumes the queue asynchronously: it confirms the payment settled (Stripe webhooks land on `/webhooks/stripe` and update the payments table), decrements reserved stock, flips the order to `confirmed`, and sends the SES confirmation email. Failures release the reservation — see the workflow view for the exact branches and the event flow for who produces and consumes each event. ## How to extend Good first forks, roughly in order of effort: - **Fulfilment/shipping service** — add a consumer on the `order-notifications` topic that books shipments and emits `order.shipped`. - **Search** — stream the products table into OpenSearch and add `GET /search`. - **Promotions** — a discount engine the orders service consults during validation; add a `promotions` table. - **Caching** — put ElastiCache (or CloudFront) in front of catalog reads; it is by far the hottest path. - **Hardening** — add a DLQ redrive runbook, idempotency keys on `POST /orders`, and Stripe webhook replay protection. When you add a service, keep the pattern: one Lambda per domain, one table set per service, cross-domain writes only via events — never let two services share a table.