Requirements
Functional Requirements:
- Allow reservation of a parking spot.
- Process payment for the reservation.
- Enable parking of a car in the reserved spot.
- Support early departure before reservation time expires.
- Gate check-in/out.
- Handle no show.
Non-Functional Requirements:
- The service should be highly available, allowing users to reserve parking spots at any time.
- The service should be reliable and consistent and should not overbook or double book parking spots.
- Gate check-in/out should be fast and complete under 10 millisecond.
- Booking can take some time and should complete in under 10 seconds.
- Giving customers leeway of 10 minutes before marking them as no show and canceling the booking.
- Send reminder notifications before reservation end time and start processing fine for delayed checkouts
- The system needs consistent and should not double book a parking spot
API Design
Our system will have the following endpoints:
- viewAvailableParkingSpots: Alows users to see a list of available parking spots based on the selected location
- reserveSpot: allows users to reserve a parking spot for a given amount of time.
- checkIn: allows users to check-in at the gate
- checkOut: allows users to check-out of the parking lot
High-Level Design
We will use an event driven architecture for this application. This allows us to easily accommodate things like bookings, check-in/outs, no shows and update the state of our application quickly.
We will also need a database with tables for users, parking spots, bookings and checkin-checkout. We can us a relational database like Postgres to store our data. We will perform joins to get the time slots where a parking lot is booked and update checkin-checkout tables when users come in and go.
We will use Stripe to process payments for parking space. We will use Kafka to handle our events and create producers like the reserveSpot, checkIn endpoints and consumers like sending confirmation email and update database services.
Application components:
- Website and UI allowing users to view and book parking spots.
- Server running our application logic
- Database to store data
Request flow:
- User views and selects a parking spot
- We begin a 5 minute timer and block the selected parking spot
- We wait for user to complete payment and receive appropriate even from Stripe
- If the payment fails we unblock the parking spot
- After payment completion, we send confirmation email and update the database
- When user checks-in we create an event to mark them as present and update database
- When user checks-out on or before booking end time we create an event and update database
- If user hasn't checked-out by booking end-time we start a timer and fine them for overstay.
On average we can assume that we will get 100 requests/minute for viewing available spots, 10 requests/minute for booking and less check-in/out. To improve performance of viewAvailableParkingSpots, we will cache booking data. We can also use read replicas of the database to improve performance.
All of our endpoints are stateless so we can containerize each service and use container orchestration to scale up and down depending on traffic.
Detailed Component Design
viewAvailableParkingSpot -
Assuming 100 requests per minute, we use the cached bookings data to show the available parking spots to users.
reserveSpot -
Before sending user to payment provider, we first check that the selected spot is available and not already booked. Otherwise we stop the process and ask the user to look for another spot. We collect details like email, start and end times for the booking etc and then send the user to Stripe to complete payment. Once we receive payment confirmation event from Stripe, we send the confirmation email to the user.
This approach prevents double booking and ensures that one parking spot is booked by just one user. We also put the parking spot in temporarily unavailable state while the payment is processing so other users can not see this parking spot as available. Depending on the booking completion we update the parking spot's status to free or unavailable in the database.
We will also need to convert the start and end times to check for availability. We can use a bitmap to encode times by dividing a day into 24, 15 minute intervals. Let's say January 1st 00:00 is index 0, we create a bitmap with 1 at start and end time indices and then compare it to the booking bitmap of the selected parking spot to determine conflicts.
checkIn -
At the gate, take booking confirmation number, validate it and allow entry up to 5 minutes before
and 10 minutes after the booking time. Update database and mark the user as present.
checkout -
Similarly at the gate take booking confirmation number and update database.
lateCheckout -
Take the confirmation number, calculate fine and accept point of sale transaction. If user is unable to pay add balance to their account.
Scalability -
All of our services are stateless so they can be easily scaled up or down. But our database is not easily scaled due to constant writes and updates. Instead we will partition the database on lot ID as information about all the parking spots in a lot should be kept together for quick access.
Potential failures -
When there is a major event near a parking spot, we can see a flood of traffic. The above discussed approach to make a parking spot temporarily unavailable when a transaction starts and the bitmap approach to check for conflicts is sufficient to prevent double bookings and the our relational database will prevent inconsistencies.