Design an Object-Oriented Shopping Cart
AmazonSoftware Engineer · Technical ScreenLast updated
Tests object-oriented design principles, domain modeling for monetary precision, extensible discount strategies, and thread-safe state mutations.
Problem Statement
Design an object-oriented in-memory shopping cart system for an e-commerce platform. The system must support core cart management, robust monetary calculations, flexible discount strategies, and auditable checkout receipts while remaining safe under concurrent user actions.
Requirements
- Money Precision: Avoid floating-point arithmetic errors. All currency amounts should be handled using integer cents or an immutable
Moneyvalue object supporting basic arithmetic and a currency code. - Cart Operations: Support adding items, removing items, and updating item quantities. Quantities must be positive integers.
- Extensible Pricing Rules: Implement a discount mechanism using the Strategy pattern so new promotions (e.g., percentage discounts, "Buy X, Get Y Free", flat order-level discounts) can be added without modifying the core cart class.
- Auditable Receipt: The cart must generate a detailed checkout summary that lists:
- Base line-item subtotals
- Itemized discounts applied (with rule identifiers)
- Final total amount
- Thread Safety: The cart must protect its internal state from race conditions during concurrent modifications (e.g., simultaneous item additions or concurrent checkout and item updates).
Proposed Interface
class Money:
def __init__(self, amount_in_cents: int, currency: str = "USD"):
...
class Item:
def __init__(self, item_id: str, name: str, price: Money):
...
class PromotionRule(ABC):
@abstractmethod
def apply(self, cart_snapshot: "CartSnapshot") -> list["AppliedDiscount"]:
...
class ShoppingCart:
def __init__(self, user_id: str):
...
def add_item(self, item: Item, quantity: int) -> None:
...
def remove_item(self, item_id: str) -> None:
...
def update_quantity(self, item_id: str, quantity: int) -> None:
...
def apply_promotions(self, rules: list[PromotionRule]) -> "Receipt":
...
Example
# Cart Setup
cart = ShoppingCart(user_id="user_123")
item1 = Item("SKU_A", "Keyboard", Money(5000)) # $50.00
item2 = Item("SKU_B", "Mouse", Money(2500)) # $25.00
cart.add_item(item1, 2) # Subtotal: $100.00
cart.add_item(item2, 1) # Subtotal: $25.00
# Rules: 10% off total if cart exceeds $100
rules = [ThresholdPercentageDiscount(threshold=Money(10000), percentage=10)]
receipt = cart.apply_promotions(rules)
# Output:
# Subtotal: $125.00
# Discounts: [AppliedDiscount(name="10% off over $100", amount=-$12.50)]
# Total: $112.50
Edge Cases to Consider
- Updating quantity to zero should remove the item or raise a defined validation exception.
- Adding items with mismatched currencies should raise a validation error.
- Multiple discounts combining to exceed the total cart value should floor the final total at 0 cents.
Hints
Hint 1
Represent currency as an integer amount in the smallest unit (such as cents) inside an immutable value object to prevent rounding anomalies.
Hint 2
Use the Strategy pattern for promotion rules, taking an immutable snapshot of the cart contents as input to avoid race conditions during calculation.
Hint 3
Synchronize cart operations with a reentrant lock (or thread-safe data structures) so that reads, updates, and receipt generation do not produce inconsistent states.
Follow-up questions
- How would you handle priority, stacking rules, and coupon conflicts when multiple overlapping promotions apply to the same cart?
- If item inventory must be reserved upon adding an item to the cart, how would you handle inventory reservation expiration and eventual consistency in a distributed system?
- How would you model the database schema to store persisted carts and ensure past order receipts remain immutable even if product prices change later?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.