Object-Oriented Design And Concurrency-Safe LLD
Asked of: Software Engineer
Last updated
What's being tested
Interviewers are probing whether you can turn an ambiguous product workflow into a clean object-oriented design, then make it safe under concurrent access. For a Software Engineer, this means more than naming classes: you must define responsibilities, state transitions, invariants, APIs, persistence boundaries, and failure behavior. Amazon cares because many production systems look “simple” at the feature level—tasks, carts, orders, reservations—but fail when multiple users, services, retries, or devices act on the same entity at once. A strong answer shows practical judgment: simple in-memory design when appropriate, database-backed consistency when needed, and extensibility without over-engineering.
Core knowledge
-
Domain modeling starts with nouns, verbs, and invariants. For a task system:
Task,User,Project,Comment,Assignee; for a restaurant:Table,Reservation,Order,MenuItem,Bill. The important part is not the nouns themselves, but who owns state and which transitions are legal. -
Single Responsibility Principle means each class should have one reason to change.
Ordercan own order-line state, but pricing rules often belong inPricingServiceorPriceCalculator; payment authorization belongs behind aPaymentProcessorinterface, not insideOrder. -
State machines are essential for workflows. Model states explicitly:
CREATED -> CONFIRMED -> PREPARING -> READY -> COMPLETED, orOPEN -> ASSIGNED -> IN_PROGRESS -> DONE. Define invalid transitions and enforce them centrally, preferably through methods likeorder.markReady()rather than arbitrary field mutation. -
Invariants are the rules that must always hold, even under concurrency. Examples: inventory cannot go below zero, one table cannot have two active reservations for the same time slot, a task cannot be assigned to a deleted user, and an order cannot be paid twice. State these aloud before choosing locks or transactions.
-
API design should expose use cases, not database tables. Prefer operations like
POST /orders/{id}/items,POST /tasks/{id}/assign, orPOST /reservations/{id}/cancelover genericPATCHfor every field when transitions have business rules. Include idempotency for retryable operations such as checkout. -
Composition over inheritance is usually better for extensibility. A pizza order should compose
Crust,Size,Topping, andPricingRule; avoid a class explosion likeLargeThinCrustPepperoniPizza. Use Strategy pattern for pricing, discounts, tax, or payment providers. -
Optimistic concurrency control works well when conflicts are rare. Add a
versioncolumn and update withWHERE id = ? AND version = ?; if zero rows are updated, reload and retry or return a conflict. This is common for task edits, cart updates, and profile-like entities. -
Pessimistic locking is appropriate when conflicts are likely or consequences are severe. In
Postgres,SELECT ... FOR UPDATEcan lock an inventory row during purchase. Keep transactions short; never hold a database lock while calling an external payment provider. -
Idempotency keys protect against duplicate client retries. For checkout, accept an
Idempotency-Keyheader and store the request key with the resultingOrderor payment attempt. If the same key is retried, return the original result instead of charging twice. -
Consistency boundaries should be explicit. A single aggregate, such as
OrderplusOrderItems, can often be updated transactionally. Cross-aggregate workflows—inventory reservation, payment, fulfillment—may need sagas, status fields, compensating actions, and reconciliation jobs rather than one giant transaction. -
Thread safety matters for in-memory components. Shared mutable structures like
Map<TaskId, Task>require synchronization,ConcurrentHashMap, immutable value objects, or actor-style ownership. Remember thatConcurrentHashMapmakes individual map operations safe, not multi-step invariants like “check then insert if capacity remains.” -
Event ordering matters in input systems and workflow systems. Keyboard/mouse events need monotonic sequence numbers, timestamps, and deterministic replay; order systems need append-only history for debugging. A log like
Event(seq, deviceId, type, payload, timestamp)helps reproduce bugs and reason about causality.
Worked example
For Design an OOD restaurant management system, a strong candidate would first clarify scope: “Are we designing for dine-in only, or also delivery? Do we need reservations, table assignment, ordering, kitchen status, billing, and payment? Is this single restaurant or multi-location?” Then they would declare assumptions: single location, multiple staff using terminals concurrently, and core flows covering reservation, seating, order placement, kitchen fulfillment, bill splitting, and payment.
The answer skeleton should have four pillars: domain model, state transitions, service/API layer, and concurrency controls. The domain model might include Restaurant, Table, Reservation, Party, Order, OrderItem, MenuItem, KitchenTicket, Bill, and Payment. State transitions matter: a Reservation moves from REQUESTED to CONFIRMED to SEATED or CANCELLED; an OrderItem moves from PLACED to IN_PREP to SERVED.
The candidate should explicitly separate responsibilities: TableManager assigns tables, OrderService mutates orders, KitchenService manages tickets, BillingService computes totals, and PaymentService integrates with a payment processor. A concrete tradeoff to flag is reservation concurrency: two hosts might try to assign the same table for overlapping times, so table assignment should happen inside a transaction with a uniqueness constraint or lock on the table/time slot. Another useful tradeoff is bill splitting: you can support equal split first, then item-level split later by modeling BillLineItem ownership.
A strong close would be: “If I had more time, I’d add audit logs for staff actions, offline-terminal sync, menu availability windows, and failure handling around partial payment authorization.”
A second angle
For Design a keyboard and mouse input system, the same design skill applies, but the domain is lower-level and more event-driven. Instead of modeling business aggregates like Order and Reservation, you model InputEvent, KeyboardEvent, PointerEvent, FocusTarget, TextComposition, and EventDispatcher. The key invariant is not “do not oversell inventory,” but “deliver events in deterministic order to the correct focused component while preserving composition and pointer semantics.”
Concurrency shows up differently: hardware events, UI thread dispatch, accessibility hooks, and replay logs may operate on different threads. A strong design would use an event queue with sequence numbers, immutable event objects, and a single-threaded dispatch loop or carefully synchronized handoff. The transferable concept is the same: define ownership of mutable state, legal transitions, ordering guarantees, and the boundary where external inputs become internal state changes.
Common pitfalls
Pitfall: Starting with a class diagram before clarifying workflows.
A tempting weak answer is to list classes like Book, User, Cart, Order, or Task immediately. That misses what interviewers care about: behavior under real operations. Start with 3–5 core flows, then derive classes and methods from those flows.
Pitfall: Treating concurrency as “add a lock” without naming the invariant.
Saying “I’ll synchronize the method” is too vague. A better answer is: “The invariant is that inventory cannot go negative; I’ll enforce it with a transaction and conditional update, UPDATE inventory SET quantity = quantity - 1 WHERE sku = ? AND quantity > 0, then check affected rows.”
Pitfall: Overusing inheritance and patterns.
Designs like VegPizza extends Pizza, CheesePizza extends VegPizza, and LargeCheesePizza extends CheesePizza become brittle fast. Prefer small interfaces, composition, and strategies: Pizza has Size, Crust, List<Topping>, and pricing rules that can evolve independently.
Connections
Interviewers often pivot from this topic into system design, especially when an object model becomes a service with persistence, caching, and APIs. They may also pivot into database transactions, distributed consistency, idempotent API design, or design patterns such as Factory, Strategy, Observer, Repository, and State.
Further reading
-
Design Patterns: Elements of Reusable Object-Oriented Software — the classic source for Strategy, Observer, Factory, and State patterns.
-
Martin Fowler, Patterns of Enterprise Application Architecture — practical patterns for service layers, repositories, transactions, and domain models.
-
Stripe API Idempotent Requests — clear real-world explanation of retry-safe API design for payment-like workflows.
Featured in interview prep guides
Practice questions
- Design object model for an elevator systemAmazon · Software Engineer · Onsite · medium
- Design a pizza ordering systemAmazon · Software Engineer · Technical Screen · medium
- Design a keyboard and mouse input systemAmazon · Software Engineer · Technical Screen · hard
- Design locker allocation serviceAmazon · Software Engineer · Technical Screen · hard
- Design a basic task management systemAmazon · Software Engineer · Technical Screen · medium
- Design an OOD restaurant management systemAmazon · Software Engineer · Onsite · medium
- Design Thread-Safe Tagging and Bookstore SystemsAmazon · Software Engineer · Onsite · hard
- Design document-tag classes with concurrency safetyAmazon · Software Engineer · Onsite · hard
- Design an online bookstore for browse and purchaseAmazon · Software Engineer · Onsite · hard
- Design keyboard and mouse input trackerAmazon · Software Engineer · Technical Screen · hard
- Design a scalable parking lot systemAmazon · Software Engineer · Technical Screen · hard
Related concepts
- Stateful Data Structures And OOP API DesignCoding & Algorithms
- Object-Oriented Design, API Design, And TestabilityCoding & Algorithms
- LLM Chat Applications, RAG, And ML EvaluationML System Design
- Distributed Systems Consistency And Low-Latency DesignSystem Design
- Mutable Data Structure DesignCoding & Algorithms
- Java, Concurrency, And Framework InternalsSoftware Engineering Fundamentals