This content originally appeared on Level Up Coding - Medium and was authored by letters from pallavi
Design a Parking Lot

Build an online parking lot management system that can support the following requirements: -
Should have multiple floors.
Multiple entries and exit gates.
A user has to collect a ticket at entry and pay at or before exit.
Allow entry for a vehicle if a spot is available for it.
Show on the display board at entry if a spot is not available.
Parking Spots of 3 types (Large, Medium, Small)
A car can only be parked at its spot. Not on any other (even larger).
A display on each floor with the status of that floor.
Fees calculated based on per hour price: e.g. 50 rs for the first hour, then 80 rs per extra hour. invoice
Small — 50, 80
Medium — 80, 100
Large — 100, 120
Steps to be followed:
- Identify all the entities (classes or models).
- Identify the properties required for these entities.
- Identify the relationships between all the entities.
- Come up with a class diagram
- Identify all the enums, abstract classes, interfaces, design patterns to be used
- Come up with the final implementation.
Let's try to follow above mentioned steps one by one: -
Let’s list down all the required entities with their properties
- ParkingLot
- Name
- Address
- ParkingFloors
- Entry Gates
- Exit Gates
2. ParkingFloor
- Floor Number
- ParkingSpots
3. Parking Spots
- Spot Number
- Spot Type — Large, Medium, Small
- Status — Occupied, Free, Out of order
4. ParkingTicket
- Ticket ID
- ParkingSpot
- Entry Time
- Vehicle
- Entry Gate
- Entry Operator
5. Invoice
- Invoice ID
- Exit Time
- ParkingTicket
- Amount
- Payment
- Payment Status
6. Payment
- Amount
- Ticket
- Type — Cash, Credit Card, UPI
- Status — Done, Pending
- Time
7. Vehicle
- License Plate
- Vehicle Type — Car, Truck, Bus, Bike, Scooter
8. ParkingAttendant
- Name
List down the cardinalities of the relationships between the classes.
- ParkingLot - ParkingFloor - One to many
- ParkingLot - ParkingGate - entryGates - One to many
- ParkingLot - ParkingGate - exitGates - One to many
- ParkingFloor - ParkingSpot - One to many
- ParkingGate - ParkingAttendant - currentGate - One to one
- ParkingSpot - ParkingTicket - One to many
- ParkingTicket - Invoice - One to one
- ParkingTicket - Vehicle - Many to one
- ParkingTicket - ParkingSpot - Many to one
- Payment - ParkingTicket - One to one
Lets draw a basic class diagram:-

Let's start with Models: -
BaseModel.java
package com.lld.parkinglot.models;
import java.sql.Date;
import lombok.Data;
@Data
public class BaseModel {
private long id;
private Date createdAt;
private Date updatedAt;
}
In order to reduce code duplication we create a base model.
ParkingLot.java
package com.lld.parkinglot.models;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
@Data
public class ParkingLot {
private String address;
private List<ParkingFloor> floors = new ArrayList<>();
private List<EntryGate> entryGates = new ArrayList<>();
private List<ExitGate> exitGates = new ArrayList<>();
}
Lets look at the gates:
package com.lld.parkinglot.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class EntryGate extends Gate {
private DisplayBoard displayBoard;
}
package com.lld.parkinglot.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ExitGate extends Gate {
}
Both entry gate and exit gate extends Gate class.
package com.lld.parkinglot.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public abstract class Gate {
private String location;
private Operator operator;
}
We have used Gate class as an abstract class because attributes like location and operator is common in both Entry gate and Exit gate.
ParkingFloor.java
package com.lld.parkinglot.models;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ParkingFloor extends BaseModel {
private Integer floorNumber;
private String name;
private List<ParkingSpot> parkingSpots = new ArrayList<>();
}
ParkingSpot.java
package com.lld.parkinglot.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ParkingSpot extends BaseModel {
private Integer spotId;
private Integer floorNumber;
private VehicleType vehicleType;
private SpotStatus spotStatus;
}
Lets look at the vehicle type
package com.lld.parkinglot.models;
public enum VehicleType {
LARGE, MEDIUM, SMALL
}
package com.lld.parkinglot.models;
public enum SpotStatus {
AVAILABLE, FILLED, OUT_OF_SERVICE
}
Whenever we have choice between boolean fields and enums, always choose enums because they are extensible.
Lets look at the ticket and payment class
package com.lld.parkinglot.models;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class Ticket extends BaseModel {
private String ticketId;
private String vehicleId;
private VehicleType vehicleType;
private Integer floorNumber;
private Integer slotNumber;
private LocalDateTime entryTime;
private LocalDateTime exitTime;
private TicketStatus status;
}
package com.lld.parkinglot.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Payment {
private String tickeId;
private Integer amount;
private String mode;
}
Lets look at the controller, services and repositories.
package com.lld.parkinglot.controllers;
import com.lld.parkinglot.dtos.GetTicketDTO;
import com.lld.parkinglot.models.VehicleType;
import com.lld.parkinglot.services.EntryGateService;
public class EntryGateController {
EntryGateService entryGateService = new EntryGateService();
public Ticket createTicket(VehicleType vehicleType) {
return entryGateService.createTicket(vehicleType);
}
}
package com.lld.parkinglot.services;
import java.sql.Date;
import java.time.LocalDateTime;
import com.lld.parkinglot.models.ParkingSpot;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.models.VehicleType;
import com.lld.parkinglot.repositories.TicketRepository;
public class TicketService {
private TicketRepository ticketRepository = new TicketRepository();
public Ticket createTicket(VehicleType vehicleType, ParkingSpot spot) {
Ticket ticket = Ticket
.builder()
.entryTime(LocalDateTime.now())
.floorNumber(spot.getFloorNumber())
.slotNumber(spot.getSpotId())
.build();
return ticketRepository.save(ticket);
}
}
package com.lld.parkinglot.services;
import com.lld.parkinglot.models.ParkingSpot;
import com.lld.parkinglot.repositories.ParkingSpotRepository;
public class ParkingSpotService {
private ParkingSpotRepository parkingSpotRepository = new ParkingSpotRepository();
public void markSlotBooked(ParkingSpot spot) {
parkingSpotRepository.save(spot);
}
}
package com.lld.parkinglot.repositories;
import java.util.ArrayList;
import java.util.List;
import com.lld.parkinglot.models.ParkingSpot;
import com.lld.parkinglot.models.SpotStatus;
import com.lld.parkinglot.models.VehicleType;
public class ParkingSpotRepository {
List<ParkingSpot> parkingSpots = new ArrayList<>();
public ParkingSpot save(ParkingSpot parkingSpot) {
parkingSpots.add(parkingSpot);
return parkingSpot;
}
public ParkingSpot findOneByVehicleTypeAndStatusAvailable(VehicleType type) {
for (ParkingSpot parkingSpot: parkingSpots) {
if (parkingSpot.getSpotStatus() == SpotStatus.AVAILABLE && parkingSpot.getVehicleType() == type){
return parkingSpot;
}
}
return null;
}
}
package com.lld.parkinglot.services;
import com.lld.parkinglot.models.ParkingSpot;
import com.lld.parkinglot.models.VehicleType;
import com.lld.parkinglot.repositories.ParkingSpotRepository;
public class SlotAllocationService {
private ParkingSpotRepository parkingSpotRepository = new ParkingSpotRepository();
public ParkingSpot allocateSlot(VehicleType vehicleType) {
return parkingSpotRepository.findOneByVehicleTypeAndStatusAvailable(vehicleType);
}
}
package com.lld.parkinglot.services;
import com.lld.parkinglot.dtos.GetTicketDTO;
import com.lld.parkinglot.models.ParkingSpot;
import com.lld.parkinglot.models.SpotStatus;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.models.VehicleType;
public class EntryGateService {
ParkingSpotService parkingSpotService = new ParkingSpotService();
SlotAllocationService allocationService = new SlotAllocationService();
TicketService ticketService = new TicketService();
public GetTicketDTO createTicket(VehicleType vehicleType) {
// Early returns
ParkingSpot parkingSpot = allocationService.allocateSlot(vehicleType);
if (parkingSpot == null) {
throw new RuntimeException("Slot not available!");
}
// Update parking spot
parkingSpot.setSpotStatus(SpotStatus.FILLED);
parkingSpotService.markSlotBooked(parkingSpot);
// Create and persist ticket
Ticket ticket = ticketService.createTicket(vehicleType, parkingSpot);
return GetTicketDTO
.builder()
.entryTime(ticket.getEntryTime())
.vehicleType(ticket.getVehicleType())
.build();
}
}
package com.lld.parkinglot.dtos;
import java.time.LocalDateTime;
import com.lld.parkinglot.models.VehicleType;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder(toBuilder = true)
public class GetTicketDTO {
private VehicleType vehicleType;
private LocalDateTime entryTime;
}
Till now we have taken a look at the ticket creation part.
Let's implement the fee calculation part for different type of vehicles. Instead of multiple if else statements we will use strategy design pattern.
package com.lld.parkinglot.services;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.strategies.FeesStrategy;
import com.lld.parkinglot.strategies.FeesStrategyFactory;
public class FeeCalculatorService {
private static final String PRICING_SCHEME = "TimeBased";
public int calculateFees(Ticket ticket) {
FeesStrategy feesStrategy = FeesStrategyFactory
.getFactory(PRICING_SCHEME)
.getStrategy(ticket.getVehicleType());
return feesStrategy.calculateFees(ticket);
}
}
Strategy:
package com.lld.parkinglot.strategies.timebased;
import com.lld.parkinglot.models.Vehicle;
import com.lld.parkinglot.models.VehicleType;
import com.lld.parkinglot.strategies.FeesCalculationFactory;
import com.lld.parkinglot.strategies.FeesStrategy;
public class TimeBasedFeesStrategyFactory implements FeesCalculationFactory {
public FeesStrategy getStrategy(VehicleType vehicleType) {
switch (vehicleType) {
case LARGE:
return new LargeVehicleTimeStrategy();
case MEDIUM:
return new MediumVehicleTimeStrategy();
case SMALL:
return new SmallVehicleTimeStrategy();
}
throw new RuntimeException("Invalid type");
}
}
package com.lld.parkinglot.strategies.timebased;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.strategies.FeesStrategy;
public class LargeVehicleTimeStrategy implements FeesStrategy {
@Override
public int calculateFees(Ticket ticket) {
// TODO Auto-generated method stub
return 0;
}
}
package com.lld.parkinglot.strategies.timebased;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.strategies.FeesStrategy;
public class MediumVehicleTimeStrategy implements FeesStrategy {
@Override
public int calculateFees(Ticket ticket) {
// TODO Auto-generated method stub
return 0;
}
}
package com.lld.parkinglot.strategies.timebased;
import com.lld.parkinglot.models.Ticket;
import com.lld.parkinglot.strategies.FeesStrategy;
public class SmallVehicleTimeStrategy implements FeesStrategy {
@Override
public int calculateFees(Ticket ticket) {
// TODO Auto-generated method stub
return 0;
}
}
package com.lld.parkinglot.strategies;
import com.lld.parkinglot.models.Ticket;
public interface FeesStrategy {
int calculateFees(Ticket ticket);
}
In this article we tried covering how to approach a Parking Lot low level design problem and explained the core implementations like creating a ticket and calculating payment fee. Let's discuss about how to handle concurrency in case of multiple ticket booking at the same time in upcoming articles.
Thank you for taking the time to check out my content. If you found this helpful, I’d really appreciate your support — feel free to leave 50 claps to show your appreciation!
Low Level Design-Part 2(Design Parking Lot) was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by letters from pallavi

letters from pallavi | Sciencx (2025-10-13T22:58:05+00:00) Low Level Design-Part 2(Design Parking Lot). Retrieved from https://www.scien.cx/2025/10/13/low-level-design-part-2design-parking-lot/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.