Requirements


Functional Requirements:


  • Manage vehicle parking across multiple floors.
  • Parking spot assignment based on vehicle size.
  • Spot availability checking.
    • If spots available, assign a random spot.
    • If spots full, tell user there are no spots available and to join a waitlist.
    • Create waitlist queue.
  • Create Ticket creation and ticket processing service.
  • Fee calculation based on parking duration. Fee's are calculated as follows:
    • 0-1 hour: $1
    • 2-3 hours: $3
    • 3-5 hours: $6
    • 5-9 hours: $9
    • 9+ hours: $24 (assume full 24 hours)


Non-Functional Requirements:

  • Handle ticket assignment race conditions - ie: 2 drivers both want spot A, both can't be assigned the same spot. Solution: 1 synchronized method for inserting driver spot permission and 1 synchronized method for deleting driver spot permission. Use a Concurrent Hashmap to handle fast reads and updates from users.
  • Handle extensibility for operations like payment methods through the adapter design pattern.


Core Objects & Relationships

Based on the requirements and use cases, identify the main objects of the system and analyze how they interact and relate to each other...


Here's a Class Diagram illustrating the relationships of the core objects and too each other:





APIs & Class Members

For each class, define the attributes (data) it will hold and the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation. Write your code in the code editor below.





Deep Dive

Explain design tradeoffs you considered. Check and explain whether your design adheres to SOLID principles. Explain how your design can handle changes in scale and whether it would be easy to extend with new functionalities. Identify areas for future improvement...


Design Tradeoffs:

  • I chose a simple object model with ParkingGarage, ParkingFloor, ParkingSpot, TicketService, Ticket, vehicle and driver because it maps closely to the real-world domain.
  • The biggest tradeoff is simplicity vs flexibility
    • For example, fee calculation could be a simple method on Ticket, but I separated it into a PricingStrategy. For the current rule set, that may be slightly overengineered, but it makes the design easier to extend later for vehicle-based pricing, event pricing, weekend pricing, or dynamic pricing.
  • Another tradeoff is concurrency granularity. Synchronizing the entire TicketService is simple and safe, but limits throughput. A better scalable design is to make individual ParkingSpot assignment atomic so multiple floors/spots can be processed concurrently.


Solid Principles:

  • Single Responsibility Principle
    • ParkingGarage manages floors and delegates ticket operations.
    • TicketService handles ticket creation and closing.
    • ParkingSpot manages spot availability and assignment.
    • PricingStrategy handles fee calculation.
    • One improvement: TicketService currently does both ticket lifecycle management and spot-finding logic. In a larger system, I would extract spot search into a separate ParkingSpotAllocationService.
  • Open/Closed Principle:
    • yes, such as pricing. Pricing is behind a pricingStrategy interface making VehicleTypePricingStrategy, EventPricingStrategy, etc, without chaning TicketService.
  • Liskov Substitution Principle:
    • Any implementation of PricingStrategy can replace another as long as it returns a valid fee for a ticket.
  • Interface Segregation Principle:

Yes. PricingStrategy is small and focused: BigDecimal calculate(Ticket ticket);

    • No class is forced to implement methods it does not need.

Future improvement:

  • The biggest improvements I would make are:
    • Extract spot allocation into a separate service or strategy.
    • Add a repository layer.
    • Improve concurrency with spot-level locking instead of service-level locking.
    • Add payment processing.
    • Replace hardcoded vehicle compatibility rules with a policy class.