An Amazon-style cart system looks deceptively simple.
At first glance:
Add item
→ apply coupon
→ checkout
→ place order
But internally, this system contains several important Low-Level Design challenges:
- mutable business state
- pricing consistency
- inventory validation
- checkout transitions
- order immutability
- workflow orchestration
And this is exactly why it’s a great domain modeling exercise.
Step 1 — Start With Business Behavior
Strong LLD does not begin with:
class Cart {}
class Order {}
class Product {}
Instead, it starts with understanding:
- lifecycle
- ownership
- state evolution
- business constraints
So let’s first understand the workflow.
Simplified Cart Flow
User adds items
→ updates quantity
→ applies coupon
→ checks out
→ payment happens
→ order gets created
Now ask carefully:
Which business objects own these responsibilities?
That question shapes the entire design.
Step 2 — Identify Core Entities
We first identify objects with:
- identity
- lifecycle
- business tracking
Product
Tracks:
- productId
- stock
- pricing
- catalog information
Entity ✅
Cart
Very important Entity.
Because Cart:
- evolves continuously
- belongs to a user
- controls item state
- changes frequently
Entity ✅
Order
Order has:
- orderId
- lifecycle
- payment relationship
- shipment state
Entity ✅
Step 3 — Identify Value Objects
Now separate descriptive values.
Money
Money(₹1200)
We care about:
- amount
- currency
not identity.
Value Object ✅
Quantity
Quantity(3)
Again:
- value matters
- identity does not
Value Object ✅
Coupon
In many beginner/intermediate systems:
- coupon behaves more like a rule/value descriptor
Example:
Coupon("SAVE10")
Often modeled as a Value Object initially.
Step 4 — Understand the Most Important Design Problem
The biggest design challenge here is:
Cart is mutable, but Order must become stable.
This distinction is extremely important.
Cart Continuously Changes
Users can:
- add items
- remove items
- update quantity
- apply discounts
Cart remains a working state.
Example:
Cart
→ evolving
→ temporary
→ editable
Order Must Become Immutable
Once checkout succeeds:
Order
→ frozen snapshot
→ historical truth
→ audit-safe
After order creation:
- totals should not randomly change
- discounts should remain preserved
- purchased item state must stay stable
This transition from mutable → immutable is a key modeling concept.
Step 5 — Identify Invariants
Now ask:
“What business rules must always remain valid?”
Examples:
- quantity cannot become negative
- cart total must equal item totals
- checkout cannot happen with empty cart
- stock must be available
- final order total must remain frozen after checkout
These invariants define system correctness.
Step 6 — Define Aggregate Boundaries
Now identify:
which objects must stay consistent together?
Cart Aggregate
Cart Aggregate
├── Cart (Root)
├── CartItems
├── Pricing Rules
└── Coupon
Cart becomes the Aggregate Root because it controls:
- item consistency
- pricing consistency
- quantity validation
- checkout eligibility
Why This Boundary Matters
Weak systems allow random modifications:
cartItem.quantity = -5;
or:
cart.total = 100;
Now consistency breaks easily.
Instead:
cart.addItem(product, quantity);
cart.calculateTotal();
The Aggregate Root protects the business rules.
Step 7 — Checkout Is a State Transition
Checkout is not merely:
- an API call
- a database insert
It is actually:
a domain state transition.
Example:
ACTIVE_CART
→ CHECKED_OUT_ORDER
This transition changes:
- ownership
- mutability
- lifecycle guarantees
Strong systems model this transition explicitly.
Step 8 — Separate Domain Logic From Workflow Logic
Now consider the complete checkout flow:
- validate cart
- reserve inventory
- process payment
- create order
- send notification
This coordination belongs inside Services.
Checkout Service
class CheckoutService {
void checkout(Cart cart) {
cart.validateCheckout();
paymentService.process();
orderService.createOrder(cart);
notificationService.send();
}
}
The Service coordinates workflow.
The Cart protects business correctness.
That separation keeps systems maintainable.
Step 9 — Think Beyond Happy Paths
Strong systems also handle:
- payment failures
- inventory shortage
- duplicate requests
- cart expiration
- coupon invalidation
Without proper state modeling:
- totals become inconsistent
- inventory becomes incorrect
- orders become unreliable
Good domain models prepare for these realities.
Final Domain Model
Entities
- Product
- Cart
- Order
Value Objects
- Money
- Quantity
- Coupon
Aggregate
- Cart Aggregate
Invariants
- valid pricing
- stock consistency
- immutable order state
Services
- CheckoutService
- PaymentService
- InventoryService
The Most Important Insight
An Amazon Cart system is not fundamentally about:
- products
- UI
- checkout buttons
At its core, it is actually:
a controlled state transition system.
The difficult part is ensuring:
- mutable business state evolves safely
- pricing remains consistent
- final orders become trustworthy snapshots
And this is exactly what strong domain modeling helps achieve.
Because good Low-Level Design is not just about organizing code.
It is about designing systems that preserve business correctness even as complexity grows.
Top comments (0)