Chapter 10: Shopping Cart Architecture
Why This Exists
The shopping cart is the most critical funnel in e-commerce. It is where browsing intent transforms into financial commitment. If the catalog is slow, a user might wait; if the cart is slow or loses their items, they will abandon the site immediately and go to a competitor. The cart architecture exists to provide a lightning-fast, highly available, and stateful experience across multiple devices, ensuring that no customer ever loses the items they intend to buy.
Real World Problem
A user is browsing an online store on their phone while riding the subway. They add a $1,500 camera to their cart. They arrive home, sit down at their laptop, log into the same website to comfortably type their credit card details, and click the cart icon. It's empty. Frustrated, they close the tab. The business just lost $1,500 because the architecture treated the cart as a temporary cookie on a single device rather than a persistent, user-centric state.
Everyday Analogy
Think of a physical shopping cart in a grocery store.
- You can put things in it, take things out, or change the quantity of apples.
- You can abandon it in the middle of Aisle 4 (Cart Abandonment).
- If you leave the store and come back the next day, the cart is gone (Session-based).
- The Digital Upgrade: Imagine if you could leave a physical cart in Aisle 4, go home, and when you walk into any other grocery store in the world, a clone of your cart is waiting for you at the entrance (Persistent Cloud Cart).
Beginner Explanation
A shopping cart is just a list of items a user wants to buy.
When you click "Add to Cart," the website sends a message to the server saying, "Remember that User 123 wants the Red Shirt." The server saves this list. Every time you view the cart, it adds up the prices of the items on the list, adds the tax, adds the shipping cost, and shows you the Total.
Intermediate Explanation
There are two main types of carts to manage:
- Anonymous Carts (Guest): The user isn't logged in. The system assigns them a temporary Session ID (stored in a browser cookie). The server maps their cart to this Session ID.
- Authenticated Carts (User): The user is logged in. The cart is tied to their User ID in the database.
Cart Merging: The architectural challenge is the crossover. If a Guest adds an item, and then logs in, the system must merge the Anonymous Cart with their existing User Cart in the database, ensuring no items are lost or duplicated.
Advanced Explanation
At scale, the cart is a Distributed State Machine. It is read and updated constantly. Using a traditional SQL database for active carts causes massive I/O bottlenecks.
Modern architectures use Key-Value In-Memory Stores (like Redis) for active carts.
- A read/write in Redis takes less than 1 millisecond.
- The Key is
cart:{user_id}. - The Value is a JSON document containing the items.
However, Redis data is volatile. If the server crashes, carts are lost. To solve this, the architecture uses Write-Behind Caching. The application writes the cart update instantly to Redis (for speed) and asynchronously publishes an event to update a persistent SQL database (for durability and data mining/abandoned cart emails).
Real World Example
Instacart's Collaborative Carts: Grocery delivery apps take cart architecture to the extreme. A husband and wife might share an account. The husband adds milk from his phone in the kitchen; the wife adds bread from her computer at work. The cart must sync via WebSockets in real-time, functioning almost like a multiplayer video game or a Google Doc, ensuring both devices instantly reflect the current state and total cost.
Architecture Design
Here is the flow of a hybrid Redis/SQL Cart Architecture:
graph TD
Client[Web / Mobile Browser] -->|Add to Cart| API[Cart API Service]
API -->|1. Fast Write & Read| Redis[(Redis - Active Carts)]
API -->|2. Async Event| MQ[Message Queue]
MQ --> Worker[Cart Persistence Worker]
Worker -->|3. Durable Save| SQL[(PostgreSQL - Saved Carts)]
Worker -->|4. Trigger 24hr Timer| Email[Marketing Service]
Email -.->|If abandoned| Abandoned[Send Recovery Email]
Database Design
1. Redis Structure (Active State):
Key: cart:user_99
Value (JSON):
{
"items": [
{ "sku": "TSH-RED", "qty": 2, "price": 20.00 }
],
"coupon_code": "SUMMER10",
"updated_at": 1690000000
}
2. SQL Structure (Durability & Analytics):
CREATE TABLE carts (
id UUID PRIMARY KEY,
user_id INT NULL,
session_id VARCHAR(255) NULL,
status VARCHAR(50), -- 'ACTIVE', 'ABANDONED', 'CONVERTED'
last_modified TIMESTAMP
);
CREATE TABLE cart_items (
cart_id UUID,
sku VARCHAR(100),
quantity INT,
FOREIGN KEY (cart_id) REFERENCES carts(id)
);
API Design
The Cart API is highly transactional (CRUD operations):
POST /api/v1/cart/items(Add item)PATCH /api/v1/cart/items/{sku}(Update quantity)DELETE /api/v1/cart/items/{sku}(Remove item)POST /api/v1/cart/merge(Merge session cart on login)
Production Considerations
- Dynamic Pricing Calculations: The cart JSON stored in Redis might say the shirt is $20. But what if the merchant changed the price to $25 while the shirt was in the cart? Before displaying the checkout page, the backend MUST re-fetch the current prices from the Catalog Source of Truth to validate the total.
- Cart Expiration: Redis keys should have a TTL (Time to Live) of, say, 30 days. You don't want to keep a 2-year-old active cart in expensive RAM.
Security Considerations
- Client-Side Pricing Trust (The Golden Rule): NEVER trust the price sent by the client. A hacker can intercept the
POST /cartrequest and change{"price": 1000}to{"price": 1}. The API must only accept theskuandquantity, and independently look up the price in the secure backend database. - Session Hijacking: If a guest cart relies solely on a guessable session cookie (like
cart_id=123), an attacker can iterate through numbers and view/hijack other users' active carts. Use secure, cryptographically random UUIDs for session tokens.
Common Mistakes
- Storing Carts Only in LocalStorage: This is fast, but it ties the cart entirely to the specific browser. Cross-device shopping (phone to laptop) breaks completely.
- Recalculating Tax/Shipping on Every Add: Tax and shipping require expensive API calls to third-party services (like Avalara for tax, FedEx for rates). Do not calculate these on every cart modification. Only calculate them when the user enters the actual Checkout phase.
Tradeoffs and Alternatives
- Stateless vs. Stateful: You could store the entire cart payload in a signed JWT cookie sent back and forth with every request (Stateless). This saves database costs but wastes massive amounts of network bandwidth if the cart contains 50 items, and limits the business's ability to run analytics on abandoned carts.
Interview Questions
- A user adds an item to their cart as a guest on their phone. They log into their account on their laptop. How do you ensure their cart is waiting for them?
- Why would you use Redis instead of PostgreSQL to power a shopping cart API?
- How do you prevent a user from hacking the network request to change the price of an item in their cart?
Hands-On Exercise
- Open your browser's Developer Tools (F12) and go to the "Application" or "Storage" tab.
- Visit a major e-commerce site as a guest and add an item to the cart.
- Look for the Cookies or LocalStorage values. Can you find the session ID or cart token they use to track your anonymous cart?
- Delete that cookie and refresh the page. Your cart should disappear.
Key Takeaways
- Carts must be highly available and fast; utilizing in-memory datastores like Redis is standard practice.
- The system must seamlessly handle the transition from an Anonymous Session to an Authenticated User (Cart Merging).
- Never trust the frontend. Prices, discounts, and totals must always be recalculated and validated by the backend.
- Carts must eventually be persisted to a durable database to enable marketing tactics like Abandoned Cart recovery.
Further Reading
- Caching Strategies in Microservices (Write-Behind Cache)
- OWASP: Session Management Cheat Sheet