Parking Lot Project Low Level Design
🚗 Parking Lot Project — Interview Summary
🎯 Purpose
This project simulates a Parking Lot Management System using Spring Boot + Core Java OOP Design Patterns.
It allows:
-
Adding parking floors and parking spots
-
Parking and unparking vehicles
-
Generating parking tickets
-
Calculating parking fees using pricing strategies
-
Making payments using different payment strategies
⚙️ High-Level Architecture
📦 1. Entity Layer (entity
package)
Represents the core data objects:
-
Vehicle
-
Contains vehicle number and type (
CAR
,BIKE
,TRUCK
)
-
-
ParkingSpot
-
Has
id
,allowedType
, andoccupied
status (AtomicBoolean for thread-safety)
-
-
ParkingFloor
-
Has
id
and multipleParkingSpot
objects in aConcurrentHashMap
-
-
Ticket
-
Contains
ticketId
,entryTime
,exitTime
,vehicle
,floorId
,spotId
, andpaymentStatus
-
⚙️ 2. Enum Layer (enumpack
package)
Defines system constants:
-
VehicleType →
CAR
,BIKE
,TRUCK
-
PaymentMode →
CASH
,UPI
,CARD
-
PricingStrategyType →
TIME_BASED
,FLAT_RATE
⚙️ 3. Strategy Layer (strategy
package)
Implements the Strategy Design Pattern.
-
Pricing Strategy (
strategy.pricing
)-
Interface:
PricingStrategy
-
Implementations:
TimeBasedPricing
,FlatRatePricing
-
Chosen via
PricingStrategyFactory
-
-
Payment Strategy (
strategy.payment
)-
Interface:
PaymentStrategy
-
Implementations:
CashPayment
,UpiPayment
,CardPayment
-
Chosen via
PaymentStrategyFactory
-
-
PaymentProcessor
-
A utility class that executes the payment using a selected
PaymentStrategy
.
-
✅ This pattern lets you switch or extend new pricing or payment logic without changing main code.
⚙️ 4. Factory Layer (factory
package)
-
VehicleFactory → creates
Vehicle
objects based on type -
PricingStrategyFactory → returns correct
PricingStrategy
instance -
PaymentStrategyFactory → returns correct
PaymentStrategy
instance
✅ This uses Factory Design Pattern to centralize object creation.
⚙️ 5. Service Layer (services
package)
-
ParkingLot (Singleton)
-
Central brain of the system
-
Maintains:
-
All
ParkingFloor
objects -
All active
Ticket
objects
-
-
Responsibilities:
-
addFloor()
-
parkVehicle()
→ finds available spot and generates ticket -
unparkVehicle()
→ calculates fee, takes payment, and vacates spot -
printStatus()
→ shows all floors and their spot statuses
-
-
✅ This uses the Singleton Design Pattern so only one ParkingLot object exists globally.
⚙️ 6. Main Runner (Spring Boot)
-
ParkingALotApplication
-
Implements
CommandLineRunner
-
On application startup, it:
-
Creates a floor with multiple spots
-
Creates vehicles and parks them
-
Prints status
-
Unparks vehicles and does payment
-
Prints final status
🔁 Logical Flow (Runtime Flow)
Here’s what happens when the app runs:
Application Start
|
v
Create ParkingLot (Singleton)
|
v
Add ParkingFloor -> Add ParkingSpots
|
v
parkVehicle(vehicle)
- findAvailableSpot() on each floor
- if found: tryOccupy() spot atomically
- create Ticket (ticketId, floorId, spotId, entryTime)
- store ticket in activeTickets
|
v
unparkVehicle(ticketId)
- get ticket from activeTickets
- calculate fee using pricingStrategy
- get paymentStrategy using PaymentStrategyFactory
- process payment
- if payment success: vacate spot, remove ticket
📐 Design Patterns Used
Pattern | Used In | Purpose |
---|---|---|
Singleton | ParkingLot | Only one global ParkingLot manager |
Factory | VehicleFactory, PaymentStrategyFactory, PricingStrategyFactory | Centralized object creation |
Strategy | Pricing & Payment modules | Easily switch algorithms at runtime |
Builder (optional) | Ticket (if Lombok used) | Object creation (replaced with setters now) |
CommandLineRunner | Main class | Bootstrapping logic on app start |
✅ Interview Selling Points
-
Thread-safe parking spots using
AtomicBoolean
-
Scalable architecture — easy to add new vehicle types, pricing models, payment types
-
Clean separation of responsibilities (SRP - Single Responsibility Principle)
-
Extensible design using Strategy + Factory patterns
-
Demonstrates real-world system design principles
1. Entry Point – Vehicle Arrives
-
A vehicle comes at the entry gate.
-
System needs to:
-
Identify vehicle type (Car, Bike, Truck).
-
Find a free spot suitable for that type.
-
👉 Handled by: ParkingLot.parkVehicle(vehicle, entryTime)
2. Finding a Spot
-
System iterates over all floors (
ParkingFloor
). -
Each floor checks its
ParkingSpot
s. -
Conditions to allocate:
-
Spot supports same vehicle type.
-
Spot is not occupied.
-
-
If free →
tryOccupy()
marks it atomically occupied (thread-safe).
👉 Outcome: A unique spot is reserved.
3. Issuing a Ticket
-
Once spot is found → create a
Ticket
. -
Ticket contains:
-
Ticket ID (UUID).
-
Vehicle details.
-
Floor ID + Spot ID.
-
Entry time.
-
-
Ticket stored in
activeTickets
map.
👉 Outcome: Driver gets a proof of parking.
4. Parking Status During Stay
-
Admin/system can check
printStatus()
. -
Shows:
-
Each floor.
-
Each spot → Occupied or Free.
-
-
Helps in monitoring.
5. Exit – Vehicle Leaves
-
Driver provides Ticket ID at exit.
-
System fetches ticket from
activeTickets
. -
If ticket invalid → reject.
👉 Handled by: ParkingLot.unparkVehicle(ticketId, exitTime, paymentMode)
6. Fee Calculation
-
System calculates charges using
PricingStrategy
. -
Example strategies:
-
Time-based → Normal vs Peak hours.
-
Event-based → Fixed per hour.
-
👉 Flexible, because it uses Strategy Pattern.
7. Payment Processing
-
Based on driver’s choice → UPI, Cash, Card.
-
System picks correct
PaymentStrategy
via factory. -
Processes payment using
PaymentProcessor
. -
If payment fails → don’t allow exit.
8. Freeing the Spot
-
If payment success:
-
Locate the spot (from ticket).
-
Call
vacate()
→ mark it free. -
Remove ticket from
activeTickets
.
-
👉 Outcome: Vehicle exits, spot becomes available.
9. End State
-
Vehicle is out.
-
Ticket no longer active.
-
Parking space ready for next vehicle.
🔄 Complete Flow Recap
-
Vehicle enters →
parkVehicle
. -
Find free spot → allocate & mark occupied.
-
Issue ticket → store in activeTickets.
-
Vehicle stays → status can be printed.
-
Vehicle exits →
unparkVehicle
. -
Fetch ticket → calculate fee.
-
Process payment (UPI/Cash/Card).
-
Free the spot → remove ticket.
-
System updated, ready for next cycle.
📐 Parking Lot – UML / Architecture Overview
1. Core Entities (Domain Layer)
Vehicle
-
Variables:
id
,type (VehicleType)
-
Methods: getters
ParkingSpot
-
Variables:
id
,allowedType
,occupied (AtomicBoolean)
-
Methods:
-
tryOccupy()
– atomically mark spot as taken -
vacate()
– free spot -
isOccupied()
– check status
-
ParkingFloor
-
Variables:
id
,spots (Map<spotId, ParkingSpot>)
-
Methods:
-
addSpot(ParkingSpot)
-
findAvailableSpot(VehicleType)
→Optional<ParkingSpot>
-
vacateSpot(String spotId)
-
Ticket
-
Variables:
ticketId
,vehicle
,entryTime
,floorId
,spotId
-
Methods: getters/setters
2. Services (Business Logic Layer)
ParkingLot (Singleton)
-
Variables:
-
floors (Map<floorId, ParkingFloor>)
-
activeTickets (Map<ticketId, Ticket>)
-
pricingStrategy (PricingStrategy)
-
-
Methods:
-
addFloor(ParkingFloor)
-
parkVehicle(Vehicle, entryTime)
→Ticket
-
unparkVehicle(ticketId, exitTime, PaymentMode)
-
printStatus()
-
3. Strategies (Behavioral Layer – Strategy Pattern)
PricingStrategy (interface)
-
calculateFee(VehicleType, entryTime, exitTime)
Implementations:
-
TimeBasedPricing – fee = hours * rate
-
FlatPricing – fixed fee
PaymentStrategy (interface)
-
pay(Ticket, fee)
Implementations:
-
CashPayment
-
CardPayment
-
UPIPayment
4. Factories (Creational Layer – Factory Pattern)
PricingStrategyFactory
-
get(PricingStrategyType)
→ returns correct strategy
PaymentStrategyFactory
-
get(PaymentMode)
→ returns correct payment strategy
5. Supporting Enums
-
VehicleType →
CAR, BIKE, TRUCK
-
GateType →
ENTRY, EXIT
-
PricingStrategyType →
TIME_BASED, FLAT
-
PaymentMode →
CASH, CARD, UPI
🚦 High-Level Flow (Revision)
-
Vehicle arrives at EntryGate
→ParkingLot.parkVehicle()
→ParkingFloor.findAvailableSpot()
→ParkingSpot.tryOccupy()
→Ticket generated
-
Vehicle exits via ExitGate
→ParkingLot.unparkVehicle()
→PricingStrategy.calculateFee()
→PaymentStrategy.pay()
→Spot.vacate()
→Ticket removed from active list
step-by-step flow
Entry flow (park)
-
Vehicle arrives at EntryGate → Gate collects vehicle info (license, type).
-
EntryGate calls
ParkingLot.parkVehicle(vehicle, entryTime)
(delegation). -
ParkingLot:
-
Iterates floors:
floor.findAvailableSpot(vehicle.getType())
-
ParkingFloor.findAvailableSpot(...)
iterates spots and callsspot.tryOccupy()
(atomic).
-
-
If a spot is found: ParkingLot creates Ticket (single responsibility):
-
ticketId
,entryTime
,vehicle
,floorId
,spotId
,paymentStatus = PENDING
-
-
Store ticket in
activeTickets
map (ticketId → Ticket). -
Return
Ticket
to EntryGate (so gate can open and hand ticket to driver).
-
-
EntryGate shows ticket and opens gate.
Notes: tryOccupy()
prevents race conditions. Only ParkingLot
writes to activeTickets
.
for (ParkingFloor floor : floors.values()) {
Optional<ParkingSpot> spotOpt = floor.findAvailableSpot(vehicle.getType());
if (spotOpt.isPresent()) {
ParkingSpot spot = spotOpt.get();
String ticketId = UUID.randomUUID().toString();
// ✅ Use setters instead of builder
Ticket ticket = new Ticket();
ticket.setTicketId(ticketId);
ticket.setVehicle(vehicle);
ticket.setEntryTime(entryTime);
ticket.setFloorId(floor.getId());
ticket.setSpotId(spot.getId());
activeTickets.put(ticketId, ticket);
System.out.println("Vehicle parked. Ticket: " + ticketId);
return ticket;
}
}
System.out.println("No spot available for vehicle type: " + vehicle.getType());
return null;
}
for (ParkingSpot spot : spots.values()) {
if (spot.getAllowedType() == vehicleType && spot.tryOccupy()) {
return Optional.of(spot);
}
}
return Optional.empty();
}
return allowedType;
}
return occupied.compareAndSet(false, true);
}
import enumpack.VehicleType;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import services.ParkingLot;
import entity.ParkingFloor;
import entity.ParkingSpot;
import entity.Vehicle;
import factory.VehicleFactory;
import enumpack.PaymentMode;
import java.time.LocalDateTime;
@SpringBootApplication
public class ParkingALotApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ParkingALotApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// Get singleton ParkingLot instance
ParkingLot parkingLot = ParkingLot.getInstance();
// Create and add a floor with spots
ParkingFloor floor1 = new ParkingFloor("F1");
floor1.addSpot(new ParkingSpot("S1", VehicleType.CAR));
floor1.addSpot(new ParkingSpot("S2", VehicleType.BIKE));
floor1.addSpot(new ParkingSpot("S3", VehicleType.TRUCK));
parkingLot.addFloor(floor1);
// Park a Car
Vehicle car = VehicleFactory.create("MH12AB1234", VehicleType.CAR);
LocalDateTime entryTime = LocalDateTime.now().minusHours(2); // parked 2 hours ago
var carTicket = parkingLot.parkVehicle(car, entryTime);
// Park a Bike
Vehicle bike = VehicleFactory.create("DL10XY9999", VehicleType.BIKE);
LocalDateTime bikeEntry = LocalDateTime.now().minusHours(1); // parked 1 hour ago
var bikeTicket = parkingLot.parkVehicle(bike, bikeEntry);
// Print parking lot status
System.out.println("\n--- Parking Lot Status ---");
parkingLot.printStatus();
// Unpark vehicles and pay
System.out.println("\n--- Unparking Vehicles ---");
if (carTicket != null) {
parkingLot.unparkVehicle(carTicket.getTicketId(), LocalDateTime.now(), PaymentMode.UPI);
}
if (bikeTicket != null) {
parkingLot.unparkVehicle(bikeTicket.getTicketId(), LocalDateTime.now(), PaymentMode.CASH);
}
// Print status after unparking
System.out.println("\n--- Parking Lot Status After Exit ---");
parkingLot.printStatus();
}
}
private final Map<String, ParkingSpot> spots = new ConcurrentHashMap<>();
Key → Value
-
Key:
spotId
(String) → unique identifier of the parking spot, e.g.,"S1"
,"S2"
,"B1"
. -
Value:
ParkingSpot
→ the actual spot object which holds: -
id
-
allowedType
(VehicleType: CAR, BIKE, TRUCK) -
occupied
(AtomicBoolean)
Why Map?
-
Fast lookup by spot ID (
spots.get(spotId)
when vacating). -
Thread-safe iteration + atomic operations (
ConcurrentHashMap
+tryOccupy()
).
floors Map
-
Key:
floorId
(String) → e.g.,"F1"
,"F2"
-
Value:
ParkingFloor
object
Example
Complete Code Link --shivamofficial/Parkinglot-LLD
...
Comments
Post a Comment