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:


  • low latency - the responses should be returned in less than 200ms
  • scalability - it should be able to scale out in case of high demand
  • availability - should have an up time of at least 99.99%
  • consistency - strong consistrency is needed so in different threads we dont have different parking lots statuses (eg in one thread park lot x is occupied but in a different thread it is free and a car can be parked there)


API Design


db:

model parking:{

status: reserved | free | taken

reservationCreatedAt?:Date

reservedFrom?: Date

reservedUntil?: Date

paidAt?: Date

leftAt?:Date

carId:String

}


GET /parkings - shows all parkings details


POST /reserve/parking/{parkingId} body:{ reservedFrom: Date, reservedUntil: Date} - used for reserving a parking lot


POST /pay/parking/{parkingId} body:{ carId:string; cardDetails:JSON } - used for paying a parking lot


POST /park/parking/{parkingId}?is_parking={is_parking}&leaving_at={leaving_at} - used for parking a car and for leaving parking earlier or on time


POST /parking/{parkingId}/status/{status} - used by admins to reset the status of a parking in case of no show


High-Level Design

reserving, parking, leaving parking, updating parking status flow:

  1. client makes call to DNS to fetch the IP of domain.
  2. DNS responds with IP
  3. client makes API call to IP address
  4. call hits load balancer - we need one in case we must scale out and add multiple instances of services.
  5. load balancer routes to parking service which fetches parking cache and in case of updates it also mutates the cache and parking db or in case cache is missing on read, it fetches data from db directly
  6. response is returned to client

payment flow:

  1. same 4 steps as above
  2. load balancer routes to payment service which call payment API and updates parking db with paidAt: Date.now() in case of succesful payment or doesnt update the parking status and user must retry payment process
  3. response is returned to client


parking service is handling reservations, leaving, parking and updating a parking lot status. it firstly checks the cache and if missing, it checks/updates the db.


payment service is call a 3rd party api called Payment API, it could be stripe. based on the response, it updates the parking paidAt date or in case of failure, it prompts the user to retry the payment


Detailed Component Design

if 2 clients try to reserve the parking spot, parking service saves to db who reserved the parking and at what time (reservationCreatedAt) so before updating that parking lot as reserved, it checks if it wasnt already reserved. only if its status is "free" it will let the second customer reserve it.


the cache will be updated on each write/update so it wont be stale and wont lead to any inconsistencies