System requirements


Functional:


  1. User Registration and Authentication:
    • Users should be able to create an account and log in securely.
  2. Parking Spot Reservation:
    • Users should be able to reserve a parking spot in advance, specifying the start and end time.
  3. Parking Spot Availability Check:
    • Users should be able to check the availability of parking spots for their desired time frame before making a reservation.
  4. Payment Processing:
    • Users should be able to make payments for their reservations via multiple payment methods (credit card, PayPal, etc.).
  5. Check In and Check Out:
    • The system should allow users to check in when they arrive and check out when they leave.
  6. Cancellation or Modification of Reservations:
    • Users should have the option to cancel or modify their reservations before the start time.
  7. Notifications and Alerts:
    • The system should send notifications regarding reservation confirmations, reminders, and cancellations.
  8. Multi-Vehicle Support:
    • The system should support various types of vehicles (cars, trucks, motorcycles) and possibly accommodate special requirements (e.g., electric vehicle charging spots).
  9. Admin Management Portal:
    • An interface for administrators to manage parking lot operations, view reservations, and handle user inquiries.
  10. Usage Reports:
  • Generating reports for management to analyze usage patterns and optimize operations.



Non-Functional:



  1. Scalability:
    • The system should be able to handle growing numbers of users and increased reservation requests, especially during peak hours or occasions (e.g., events or holidays).
  2. Availability:
    • The system should ensure high availability, ideally 99.9% uptime. Users should be able to access the system at any time without significant downtime.
  3. Performance:
    • The system should have quick response times for user actions, such as checking availability (within milliseconds), making reservations, and processing payments.
  4. Security:
    • The system should implement strong security measures, including user authentication, secure payment processing (PCI compliance), and protection against data breaches.
  5. Usability:
    • The user interface should be intuitive and user-friendly, allowing customers to navigate through the functionalities easily and complete their tasks with minimal steps.
  6. Maintainability:
    • The system should be designed in a way that allows for easy updates, bug fixes, and feature enhancements without major downtime or disruption.
  7. Data Consistency:
    • The system must ensure that all data is consistent, particularly for reservations. There should be no double reservations or data integrity issues.
  8. Fault Tolerance:
    • The system should have a plan for handling failures gracefully, ensuring that users can still access necessary functionalities even in the event of a component failure.
  9. Audit and Monitoring:
    • There should be logging and monitoring tools to track system performance, user activities, and any errors that occur, allowing administrators to respond to issues proactively.
  10. Compliance:
    • The system should comply with relevant regulations and standards (e.g., GDPR for data protection, local laws regarding parking services).





Capacity estimation


  1. Number of Parking Lots:
    • The company manages 100 parking lots on average in each country.
  2. Average Capacity per Parking Lot:
    • Each parking lot has a capacity of approximately 200 cars.

Based on these assumptions, here’s the breakdown:

  • Total Number of Parking Lots: 100 parking lots per country
  • If we consider operations in 10 countries, the total number of parking lots would be:
    • 1000 parking lots
  • Total Parking Spots Across All Lots:
    • 200,000 parking spots




API design

Define what APIs are expected from the system...


API Endpoint: /accounts

Method: POST

Body:

{ "email": ... "password: ... }


Response:

Appropriate Status Code

Body:

{ "userId": ... }


API Endpoint: /parkingspots

Method: GET

Example Request showing filter parameters:

GET /parkingspots?parking-lot-id=123&start-at=2025-01-16T13:00:00&end-at=2025-01-16T15:00:00&vehicle-type=car


Response:

Appropriate Status Code

Body:

{ "availableSpots: [ { "spotId": "A1", "lotId": "123", "vehicleType": "car", "reserved": false }, { "spotId": "A2", "lotId": "123", "vehicleType": "car", "reserved": false }, ] }


API Endpoint: /reservations

Method: POST

Authorization Header: JWT

Body:

{ "lotId": 123, "spotId": "A1", "startAt": timestamp, "endAt": timestamp, "vehicleType": "car" }


Response

Appropriate Status Code

Body

{ "reservationID": 56464 }


API Endpoint: /reservations

Method: POST

Authorization Header: JWT

Body:

{ "reservationId": 56464, "paymentMethod": "creditCard", "paymentToken": token }


Response:

Appropriate Status Code

{ "transactionId": 123, "status": "paid" }


checkIn()

Input: reservation_id, authentication

Output: parking_session_id


checkOut()

Input: parking_session_id, authentication

Output: parking_session_id


cancelReservation()

Input: reservation_id, authentication

Output: cancellation_id


updateReservation()

Input: reservation_id, new_start_at, new_end_at, new_vehicle_type, new_parking_spot_id

Output: reservation_id




Database design

Defining the system data model early on will clarify how data will flow among different components of the system. Also you could draw an ER diagram using the diagramming tool to enhance your design...


table: users

columns:

  • id primary_key
  • email string
  • encrypted_password string


table: parking_lots

columns:

  • id primary_key
  • location string


table parking_spots

columns:

  • id primary_key
  • parking_lot_id foreign_key
  • vehicle_type string


table reservations

columns:

  • id primary_key
  • parking_spot_id foreign_key
  • vehicle_type string
  • start_at timestamp
  • end_at timestamp
  • user_id foreign_key


table cancllations

columns:

  • id primary_key
  • reservation_id foreign_key
  • cancelled_at timestamp


table: parking_sessions

columns:

  • id primary_key
  • reservation_id foreign_key
  • started_at timestamp
  • ended_at timestamp




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...



The high level design is shown in the diagram.




Request flows

Explain how the request flows from end to end in your high level design. Also you could draw a sequence diagram using the diagramming tool to enhance your explanation...



One thing I want to highlight is the notification service, which can be used to notify the user when a reserved spot becomes available. Whenever there is a cancellation or another user checks out of a particular spot, the reservation service can place an event on the message queue with the parking_spot_id. The notification service will read those events from the queue, and if there is a user waiting on that spot to become available, the notification service can trigger an appropriate notification to the user such as a push notification or SMS message. The notification service will need access to the read replica of the reservation database to check for users waiting on the spot, and will also need access to the user database to check for the user's notification settings.


I'd also like to highlight the request flow when a user is paying for a reservation. The payment service will be responsible for handling interactions with third party payment gateways, and ensuring any card information provided by the user is stored in a PCI compliant manner. If a payment fails or is later reversed, the payment service should also communicate with the reservation service to make sure any reservation that has been made is cancelled and the user notified of the failure.


Detailed component design

Dig deeper into 2-3 components and explain in detail how they work. For example, how well does each component scale? Any relevant algorithm or data structure you like to use for a component? Also you could draw a diagram using the diagramming tool to enhance your design...


The reservation service and reservation database need to handle contention for multiple users attempting to book the same parking spot at once. One choice for handling this contention would be acquiring a database lock on the row for the parking space being reserved so that the database will not allow concurrent reservations of the same parking spot since only one instance of the reservation service can hold the lock on a particular spot at a time. If another instance of the reservation service fails to acquire the lock and the spot is booked before the reservation can be made, the service would be responsible for communicating that failure to the user so the user can attempt to reserve another spot or the same spot at a different time.


While the lock above would address contention for the same spot, it could negatively impact performance since two reservations cannot be made on the same spot at a time, even if the reservations times may not conflict with one another. If we want to allow multiple reservations for the same parking spot simultaneously so long as the times do not conflict, we will need a different approach. One approach may be optimistic locking, where the reservation service proceeds with making the write to the database without acquiring a lock, and then checks after the write is confirmed to ensure that no competing reservations were made in the time the write was in progress. If no collisions occurred, the reservation service can leave the write in place and confirm the reservation with the user. If a conflicting write occurred, the reservation service would be responsible for undoing the write and informing the user of the failure.


To meet latency requirements, we want to make sure reads to check parking spot availability are fast. One approach to this may be to direct all parking spot availability checks to the read replica of the reservation database, which will also reduce load on the main reservation database since it would only be handling writes. While this would introduce the possibility of serving the user stale parking spot availability information due to replication lag from the main and the read replica, this may be acceptable since the reservation service will be handling any incorrect bookings made of spots that turn out to not in fact be available.


Another approach if latency is still too slow reading from the read replica would be to introduce a cache that the reservation service will check first when serving requests for parking spot availability. Based on usage patterns, we might consider having the cache store availability information for today's date and the following week, if users are most commonly reserving spots in the short term rather than far in advance. To keep the cache up to date, we could use something like a write-through strategy whenever a reservation is made so that the cache is updated by the reservation service immediately after a new reservation is written to the reservation database.



Trade offs/Tech choices

Explain any trade offs you have made and why you made certain tech choices...


Given the capacity estimates above, we should be able to concurrently handle reservations in a single reservation database. If the company were to significantly scale up to the point where a single reservation database was no longer sufficient, there would be a few options. The most straightforward would likely be to shard data into multiple reservation databases based on geographic region. Assuming the geographic regions are reasonably large, most users will likely not be booking parking reservations across multiple regions. However for use cases where we did want to present the user a history of all their reservations, and the user had parked in parking lots stored in separate databases, the reservation service would need to be prepared at the application layer to join together the appropriate data.


We could also scale the reservation database horizontally, and in that case we would need to select select appropriate coordination mechanisms among the instances of the reservation database, and choose between consistency or availability. If it is most important that a user never reserve a parking spot that is already booked by someone else, we should favor consistency over availability. If it is most important that a user never attempt to submit a reservation and receive an error message that the reservation could not be completed, we should favor availability.


Given the significant negative user experience of booking a spot that is already reserved, I would recommend favoring consistency. To ensure consistency across multiple instances of the reservation database, any writes creating new reservations need to be written to a sufficient number of databases in the cluster before the write is considered a success and the response is returned by the reservation service to the user. This consistency will come at the cost of increased latency.


The diagram shows separate databases for the authentication service and the payment/reservations services. This separation of the databases will allow each to be scaled independently, and to be optimized for different read/write patterns. The user database can also follow more strict security policies. However, this separation comes with added complexity that may not be warranted at the estimated capacity. By separating this data, the application layer will have additional work to do to ensure appropriate user ids are passed through to the payment/reservation services after user authentication occurs, and the application layer may need to handle joining together data across the separate databases.



Failure scenarios/bottlenecks

Try to discuss as many failure scenarios/bottlenecks as possible.


Any of the items shown in the diagram from the API Gateway down could be a point of failure. To ensure appropriate uptime, we would need to select an appropriate strategy where either a backup API Gateway can be promoted and traffic routed to the promoted API Gateway or where there are multiple active API Gateways at a time and traffic is routed between them.


For each of the services, multiple instances can run at a time so that no one service is a single point of failure. A solution such as Kubernetes can manage ensuring an appropriate number of instances of each service are running at any given time, or the API Gateway would need to be able to discover the instances of each service and be able to direct traffic among the instances appropriately.


For each of the databases, we should have replica databases ready to be promoted if the active database goes down, and appropriate backups kept so that data is not lost on failure of a database instance.



Future improvements

What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?