Our system will have the following endpoints:
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:
Request flow:
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.
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.