System requirements
Functional:
- Parking system should account for multiple levels
- Each level should have many spots
- Each floor should be displayed on the app to show which spots are available by floor
- Parking system must be able to handle payment
- There should be options on this app to handle multiple lot types (regular car spot, large car spot, electric car spot, handicap spot etc)
- Electric car spots should be able to pay to charge their car
- Parking lot should have multiple entrances
- User can pay by cash or card
Non-Functional:
- Should have strong consistency
- Should be able to handle large scale
- Good security
Capacity estimation
- I assume this app will be available in 10 countries to start off
- Each country has an average of 100 parking garage
- Lets assume average time for a spot is 4 hours
- Average parking garage has 200 spots
- Each day generates (24/4) * 200 * 100 * 10 = 1200000 tickets
- Estimating ticket storage:
- ticket_id (20 bytes)
- receipt_id(20 bytes)
- parking_lot_id(20 bytes)
- status( 1 byte)
- timeStart(8 bytes)
- timeEnd(8 bytes)
around 80 bytes
Each day produces 96mb so 34GB a year of data
API design
enter_garage(vehicleType, parking_lot_id): Return a number which can be a ticket id
exit_garage(parking_lot_id,reciept_id, vehicle_type): return True if a ticket has been paid or False if not
show_capacity(parking_lot_id): return an integer of how many spots are left in this parking lot
pay(parking_lot_id, ticket_id): return a receipt number.
Database design
User: {
ticketId: String,
parkingLotId: string,
timeStart: datetime,
timeEnd: datetime,
receiptId: string,
status: (using, left) string
}
lotCapacity: {
lotCapacityId: string,
lotType : (electric, handicap, etc) string,
spot_capacity: int,
spots_used: int,
}
transaction: {
receiptId: string,
ticketId: string,
paymentMethod: string,
paymentTime: datetime,
status: (paid, confirmed) string
}
parkingLot: {
parkingLotId: string,
lotCapacityId: string,
status
}
High-level design
- CDN will boost the load static assets based on location
- Load balancer will help manage traffic to a particular server
- Parking service will handle a vehicle's enter and exit as well as payment
- Payment service will receive a request from the message queue and then store this payment information in the database. The payment status will be sent back to the parking service and send a notification
- Notification service provides real time updates on the parking spots and payment results
Request flows
- Client first checks capacity at entrance and the parking service returns the result
- Client enters the lot and enter_garage is invoked and client gets a ticketId
- Before exiting, client will need to pay and invokes the pay() and gets a payment result
- Client will leave the lot and invokes exit_garage() method, it takes ticketId as input and checks if the client has paid for the ticket
Detailed component design
The payment service may go down so needs to be designed for when users are paying a fee. When the user submits a payment request for the parking lot, the parking service will generate a request in the message queue and also generate a receipt number for the user which will be stored in the transaction table. If the payment service is offline, the parking service will check the transaction table for the receipt existance, if it does then they will be able to leave. After the payment service is back online, it will return the payment status to the parking_service and parking_service will update the receipt that are in paid status to confirmed status. This approach actually implements a local payment record to keep track of the user parking status.
Trade offs/Tech choices
The biggest decision point was the database. For this service, I chose Relational Database over NoSQL Database. Because RDB provides strong consistency, which is beneficial for a payment service. Users can not leave util they pay for their parking fee, which is need to be 100% sure in this situation.
NoSQL database, for example MongoDB, would provide a better horizontal scalability than RDB. But for this service, I decided the benefit of RDB outweighs that of NoSQL DB.
Failure scenarios/bottlenecks
- I think when a super show ends, a large number of uses will pay for their fee, at the same time the MQ and the payment service will be very stressful. To mitigate this problem, payment service should be planning to scale up to a certain capacity to consume all the request received from the MQ. MQ should also be prepared to scale up, to prevent a large number of message arrival.
- This system relies on the ticket very much. It is possible If the users lost their ticket. To handle this situation, we need to establish a policy for lost tickets that includes potential fees or penalties. Users might be charged a flat rate or be required to pay the maximum daily fee if they cannot present a ticket.
Future improvements
- A spot reservation feature should be considered.
- parking allocation is also useful for users, in order to save the time looking for available parking spot.