Problem Statement
What to Design?
Design a web service that helps customers purchase the cheapest available copy of a book from a network of online booksellers.
A customer submits a request including the book title, their shipping and payment details, and a maximum price they're willing to pay.
The service then queries multiple booksellers:
- if any offer the book at or below the specified price, it automatically purchases the cheapest option and later informs the customer of the seller and final price.
- if no offer meets the price threshold, the service notifies the customer of the lowest available price instead.
Use Case Assumptions
- book names are unique (like a UID)
- the customer only ever buys one copy of a book at a time
- the booksellers may or may not all sell the same books
- for any specific book, each bookseller may charge a different price
- sellers provide heterogeneous APIs (different request/response formats)
Functional Requirements
FR1 – Users should be able to submit a book purchase request with a maximum price limit
"As a user, I want to request a book by providing its name, my shipping and payment information, and a maximum price I'm willing to pay."
FR2 – Users should be able to retrieve a price resolution result (either success or lowest available price)
"As a user, I want to know whether the system found a seller within my budget — or at least what the lowest price available is."
FR3 – Users should be able to trigger an automatic purchase when the offer is acceptable
"As a user, I want the system to automatically purchase the book if the price is acceptable — without additional confirmation."
Non-Functional Requirements
We rank this list of NFRs based on priorities tied to this specific design.
NFR1 – Efficiency – avoid unnecessary work and reduce cost per request
Challenge Yourself - What can you think of to improve cost efficiency?
Think about:
- Direct seller API normalization utilities
- Early-exit fan-out
- Tiered seller groups (call fast/reliable sellers first)
- Deduping concurrent fan-outs
- Stale-offer reuse (cache)
- Dynamic skipping of slow or unstable sellers
Without adapters, we must embed seller-specific call logic directly into workers — making efficiency even more important.
NFR2 – Latency – p90 < 5 seconds
NFR3 – Idempotency – avoid duplicate charges or purchases
NFR4 – Scalability – up to 200 QPS, 20K seller calls/sec
How do we get this number? You can also practice to estimate yourself!
Let's just suppose the scale assumptions below:
- 1M DAU
- Average: ~0.01–0.1 requests/user/day (since users only search occasionally) ⇒ 1M DAU * 0.1 requests/user/day / 10^5 secs = 10 QPS on average (or Low)
- Estimated traffic:
- Low: 10 QPS
- Peak: 10 - 20x ⇒ 100–200 QPS sustained (e.g. promo days or peak hours)
- Each request → 50–200 seller fan-out ⇒ 5K–20K external calls/sec during peak
NFR5 – Reliability – tolerate partial failures and degraded seller availability
NFR6 – Observability – end-to-end traceability and real-time metrics
Why we take the ranking like this?
We rank Efficiency highest because the system's core value is to help users find a cheap price fast — and that means minimizing unnecessary work: exiting early, prioritizing high-value sellers, batching calls, and de-duplicating in-flight lookups. Latency is next, as timely responses drive user trust and satisfaction. Idempotency comes third because this system involves real purchases — without safeguards, retries could trigger double charges or duplicate orders. Scalability is essential to handle peak traffic and fan-out volume. Reliability ensures users still get useful results even when sellers fail. Observability, while operationally critical, has the least direct impact on the user experience and can be layered in post-MVP.
Requirement Summary
| Functional Requirements (FRs) | |
|---|---|
| Name | Description |
1. Submit Purchase Request | Users submit a book purchase request with a maximum price limit. |
2. Retrieve Price Resolution | Users retrieve a price resolution result (either success or lowest available price). |
3. Automatic Purchase | System automatically triggers purchase when the offer is acceptable. |
| Non-Functional Requirements (NFRs) | |
|---|---|
| Name | Description |
1. Efficiency | Avoid unnecessary work and reduce cost per request. |
2. Latency | p90 < 5 seconds response time. |
3. Idempotency | Avoid duplicate charges or purchases. |
4. Scalability | Up to 200 QPS, 20K seller calls/sec. |
5. Reliability | Tolerate partial failures and degraded seller availability. |
6. Observability | End-to-end traceability and real-time metrics. |
High-Level Design
How to structure your High Level Design?
When presenting your High-Level Design, a clear and effective strategy is to go vertically, one Functional Requirement at a time — and walk through a working, end-to-end solution for each.
For every FR, answer:
- What APIs are called or exposed at this stage?
- What entity or schema design is needed to support the flow?
- What are the core steps and flow from user input to system output?
- What components are involved (e.g., API gateway, queue, fan-out workers, storage, aggregator)?
This vertical approach keeps each requirement self-contained and actionable, making it easier to read, review, and debug. For documentation, you can co-locate entity and API definitions within each FR, or collect them in a single section at the beginning — both are valid depending on interview style or presentation format.