System requirements


Functional:

  1. Users are able to see a list of cinema halls available post selecting city.
  2. Users are able to check list of all shows post selecting cinema hall . ie (Movie 1 - 1PM, 3PM)
  3. Users are able to select a particular seat and book it post selecting the show.
  4. Users are notified with the show ticket on email.
  5. Users are able to rate their experience post the show. They are given review link post the show.


Non-Functional:

  1. Highly consistent that is No double booking for a single seat.
  2. High performance and low latency for listing and bookings.
  3. Durability - No data is lost.
  4. Scalability - 100 daily active users.



Capacity estimation


Consider Average


  1. 100 M daily users and booking 1 tickets per week per user .. ~ 4 tickets a month ~ 50 tickets a year.
  2. 14M tickets per day ~ 200 req/ sec. 200 tickets booked every second.
  3. Assuming visitor are 5x of bookers so 1000 req/sec.


Considering peak

7 times the above so

Booking at peak = ~ 1400 req/sec

Visitor at peak = ~ 7000 req/sec


Storage:

Storage for booking in 1 year = 400 in one month = 4800 M in year

Booking need 200 bytes of storage ~ 100 * 10 * 1000 * M ~ 1TB a single instance can easily have this.



API design


GET /api/v1/CinemaHall/{City}

GET /api/v1/Shows/{City}/{CinemaId}

GET /api/v1/Seats/{showId}


POST /api/v1/BookSeat

-d {

showId:

UserdId:

SeatIds List<>

}


POST /api/v1/review -d

{

showId

rating:

reviewDescription:

userId:

}




Database design

CinemaHall

  • Id -BIGINT
  • City -
  • Address
  • lat/long - location -
  • Rating
  • Owner


ScreenHall

  • Id - PK
  • CinenaHall -pk
  • ScreenName
  • Capacity


Seats

  • ScreenId - PK
  • SeatId -PK
  • RowNumber
  • ColNumber
  • Type


Movie:

  • Id -PK
  • Name
  • trailerUrl
  • IMDBRating
  • Cast List
  • bannerUrl


MoveShow

  • ShowId - pk
  • CinemaHallId
  • ScreenId
  • StarTime
  • MovieId
  • Duration


Booking

  • BookingId - PK
  • UserId
  • ShowId
  • Status <Created, In-Progress, Cancelled, Booked>
  • BookingTime
  • totalCost


SeatInstances

  • ShowId - PK
  • SeatId - PK
  • Status - <On-Hold, Booked, Available> Default - Available.
  • cost
  • BookingID


High-level design

You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...







Request flows

Booking Flow


  1. Client will request the booking(userId, ShowId, List<>SeatId)
  2. The booking service will first take a pg advisory lock on the showId and seatId and will place the request in a created state with seats on hold.
  3. Inside a TRANSACTION (Sample for 3 seats in a single booking)-

BEGIN

select pg_advistory_lock('showId-seatId1);

select pg_advistory_lock('showId-seatId2);

select pg_advistory_lock('showId-seatId3);


Insert into booking values(12, userId showid, 'Created'., Now(), 1500);

Insert into bookingSeat values(12, showid, 'A1'., 'On-Hold', 500);

Insert into bookingSeat values(12, showid, 'A2'., 'On-Hold', 500);

Insert into bookingSeat values(12, showid, 'A3'., 'On-Hold', 500); COMMIT;

  1. Booking service asks the payment service for a unique payment for by providing the booking id and returns the booking Id and the payment url to the frontend.
  2. Frontend redirect the user to the payment link and once the payment is completed via. payment service, which updates the status of booking the payment table and informs the booking service via it's call back url.
  3. Booking service updates the status of seats to Booked.
  4. It will place the booking in a kafka queue. The notification service, will create a ticket and will send it to user on email.


Listing Flow:

  1. List of movie shows, cinema halls will be given by using the listing service.
  2. This data will be cached using an external cache like Redis.




Detailed component design

API gateway: The api gateway will redirect the request to multiple services based the request url. It will also do load balancing between the instances of a single service.


User Service: This will handle user login/signup. Manages user profiler.

Listing Service: This will be responsible for the read flow, eg listing movie halls and movie shows.

Booking Service: This will handle the bookings with the help of payment service.

Message Queue: The booking will be persisted in the message queue before the invoice and tickets are created and sent to user.

Notification Service: This will be responsible for sending invoice and booking confirmation.

Cache: A redis cache will be added to store the list api request and responses. This will be LRU based cache.



Trade offs/Tech choices

  1. Database: We will be using a relational database like Postgresql for below reasons. High consistency. Postgresql provides the right level of isolation and locking mechanism for handling the booking in isolated manner by the multiple concurrent users. It can be scaled to handle the ~7000 qps of read traffic by adding replicas. It provides support for geospatial queries to handle future use case of listing cinema halls near the user. It also provides good performance for both read and write.
  2. Caching: This will help with performance enhancement by caching for read requests. This will also help with managing scale at peak hours. This will be an LRU based caching and redis managed instance will be used. This can scale and meet our future traffic growth.
  3. Message Queue : We added this to make the system more available and decoupled so that as soon as the booking is done. we can repond to the client above the booking done status and the notification for invoice can come later. Kafka will be the right choice for it's high durability.
  4. Keeping List and Booking Service Separate. We have created two service one for Listinga and other bookings. This is done to interdependently scale the two services.





Failure scenarios/bottlenecks

  1. Having a single instance of listing and booking service.
  2. Notification service going down and not reading messages from the queue.
  3. Postgresql primary - crashing
  4. Postgresql





Future improvements

  1. To avoid the single instances of service and improving the system availability each will have 3 instances running in different availability zone.
  2. Postgresql primary can fail or can be slowed with too many read requests. We will add read replicas so that it can failover to teh replica.