Requirements
Functional Requirements:
- Users can see available shows.
- Users can view a seating map to pick seats.
Non-Functional Requirements:
- Low latency
- Can scale if necessary
- Reliability: no double-booking
API Design
- get_shows
- get_seats
- reserve_seats
- payment
High-Level Design
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
The backend server exposes 4 above APIs to client
- get_shows and get_seats are simple read APIs
- seat state: AVAILABLE, PENDING, RESERVED
- reserve_seats API: mark the seat as "PENDING" in database, and do not show "PENDING" seats, then move the user to payment API
- payment API let user select their favourite payment gateway and wraps user details and redirect to 3rd party payment gateway
Detailed Component Design
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
Reserve seat / payment details to ensure no double-booking:
- Implement a webhook in server to handle payment logic:
- Success: update "PENDING" seat to "RESERVED" and send booking to user email
- Fail: update seat to "AVAILABLE"
- To prevent an user from holding a seat for too long, set expire time for pending and have a separate process on server to periodically clean up expired "PENDING" seat. Have a logic in server to refund to user if a pending seat is expired (now state = "AVAILABLE") due to delay in payment gateway.
- To reduce database read in each reserve_seats call, we also write seatId -> userId mapping to Redis when user reserve_seats (besides writing to DB), this represents "PENDING" seat. Then in user flow, after user select seats, the reserve_seats API will first check the Redis cache if this seat is held by another user, if yes, raise error and ask the user to select another seat, else check & write to DB & add the entry to Redis cache and move user to payment API.
Database design:
- get_shows and get_seats are read-heavy. If the service has high load, we can scale by slave nodes or cache the content in Redis. If have images / videos, can move the media content to CDN.
Rate limit (e.g. each user can booked multiple seats, but can hold only 1 seat pending):
- To prevent user from holding too many seats at the same time, we store the mapping from userId => pending_seat in a cache (Redis). We check this cache in reserve_seats API to ensure user is not currently holding any seat. Set this cache to expire (same time as pending seat threshold) and also need to ensure cache update upon payment.