๐ Designing the Perfect Parking Lot System: A Complete Guide

Ever wondered how modern parking lots seamlessly manage hundreds of vehicles while keeping track of every spot, payment, and customer? Let's dive into the fascinating world of parking lot system design!
๐ฏ The Challenge
Picture this: You're tasked with designing a parking lot system for a busy shopping mall. It needs to handle multiple floors, different vehicle types, various payment methods, and provide real-time information to drivers. Sounds complex? It is! But with the right approach, we can break it down into manageable components.
๐ What We Need to Achieve
Functional Requirements Breakdown
Our parking lot system needs to be a multi-talented performer:
๐ข Multi-Floor Management
Handle multiple levels of parking
Track capacity per floor
Direct traffic efficiently between floors
๐ช Smart Entry & Exit System
Multiple entry/exit points to prevent bottlenecks
Automated ticket dispensing
License plate recognition (future enhancement)
๐ซ Ticketing System
Generate unique tickets with timestamps
Track parking duration
Secure ticket validation
๐ณ Flexible Payment Options
Cash payments at automated machines
Credit/debit card processing
Customer service portal for assistance
Mobile app integration (future scope)
๐ Intelligent Capacity Management
Real-time occupancy tracking
Prevent overcrowding
Queue management during peak hours
๐ ฟ๏ธ Diverse Parking Spots
Because one size doesn't fit all:
Compact spots - For smaller vehicles
Large spots - For SUVs and trucks
Handicapped spots - ADA compliant with easy access
Motorcycle spots - Optimized for two-wheelers
Electric vehicle spots - With charging infrastructure
โก Electric Vehicle Support
The future is electric, and we're ready:
Charging stations integrated with payment systems
Different charging speeds (Level 1, 2, and DC fast charging)
Real-time charging status monitoring
๐บ Information Display System
LED boards showing available spots per floor
Entry point displays with real-time updates
Mobile app with live availability
๐ฐ Dynamic Pricing Model
Hourly rate structure
Peak/off-peak pricing
Early bird discounts
Monthly pass options
๐๏ธ System Architecture Design
Why This Approach?
I'm designing this system with a microservices architecture because it offers:
Scalability: Each component can scale independently
Maintainability: Easy to update individual services
Reliability: If one service fails, others continue working
Technology Flexibility: Different services can use different tech stacks
Core Components
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PARKING LOT SYSTEM โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Entry โ โ Display โ โ Exit โ โ
โ โ Management โ โ Service โ โ Management โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Parking โ โ Central โ โ Payment โ โ
โ โ Spot โโโโค Management โโโบโ Service โ โ
โ โ Management โ โ System โ โ โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Vehicle โ โ Charging โ โ Notificationโ โ
โ โ Management โ โ Station โ โ Service โ โ
โ โ โ โ Management โ โ โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ง Detailed Component Design
1. Entry Management System
Why this approach? We need a robust entry system that can handle high traffic while maintaining security and accuracy.
Key Features:
Automated ticket dispensing
Vehicle type detection (cameras + sensors)
Real-time capacity checking
Queue management
public class EntryGate {
private ParkingLot parkingLot;
private TicketGenerator ticketGenerator;
private GateController gateController;
public Ticket processVehicleEntry(VehicleType vehicleType) {
if (parkingLot.hasAvailableSpots(vehicleType)) {
Ticket ticket = ticketGenerator.generateTicket(vehicleType);
gateController.openGate();
parkingLot.updateOccupancy(vehicleType, 1);
logEntry(ticket);
return ticket;
} else {
displayMessage("LOT FULL - Please try alternative parking");
return null;
}
}
private void logEntry(Ticket ticket) {
System.out.println("Vehicle entered: " + ticket.getTicketId() +
" at " + ticket.getEntryTime());
}
}
2. Parking Spot Management
Our Strategy: We'll use a hierarchical approach - Building โ Floor โ Zone โ Spot. This makes the system incredibly scalable.
Spot Types & Specifications
| Spot Type | Dimensions | Special Features |
| ๐ Compact | 8' ร 16' | Standard parking |
| ๐ Large | 9' ร 20' | For SUVs/Trucks |
| โฟ Handicapped | 11' ร 20' | Wider access + ramp |
| ๐๏ธ Motorcycle | 4' ร 8' | Secured area |
| โก Electric | 9' ร 18' | Charging station |
Why these dimensions? Based on standard automotive sizes and accessibility requirements, ensuring comfortable parking while maximizing capacity.
3. Payment Processing System
Multi-Modal Payment Strategy:
Payment Flow:
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Ticket โโโโโบโ Calculate โโโโโบโ Payment โ
โ Validation โ โ Fee โ โ Processing โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Barcode โ โ Hourly โ โ Cash โ
โ Scanner โ โ Rates โ โ Credit โ
โ โ โ Peak/Off โ โ Mobile โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
4. Electric Vehicle Charging Integration
Why prioritize EV support? With the automotive industry's shift toward electrification, EV support isn't just nice-to-haveโit's essential for future-proofing.
Charging Station Features:
Multiple connector types (Type 1, Type 2, CCS, CHAdeMO)
Integrated payment processing
Real-time charging status
Mobile app notifications
5. Real-Time Display System
Information Architecture:
Entry Display Board:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PARKING AVAILABILITY โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ FLOOR 1: ๐ 12 ๐ 5 โก 3 โ
โ FLOOR 2: ๐ 8 ๐ 2 โก 1 |
โ FLOOR 3: ๐ 15 ๐ 7 โก 4 |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โฟ HANDICAPPED: 6 AVAILABLE โ
โ ๐๏ธ MOTORCYCLE: 12 AVAILABLE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ System Workflow
The Complete Journey
๐ Vehicle Approaches
License plate recognition (optional)
Vehicle type detection
Display available spots
๐ซ Entry Process
Ticket generation with QR code
Barrier opens automatically
Real-time capacity update
๐ ฟ๏ธ Parking Assignment
Smart routing to available spots
Floor-specific guidance
Spot reservation (premium feature)
โฐ Duration Tracking
Continuous monitoring
Overstay alerts
Dynamic pricing calculation
๐ณ Payment Processing
Multiple payment options
Receipt generation
Grace period for exit
๐ช Exit Process
Ticket validation
Payment confirmation
Automated barrier opening
๐ Scalability & Performance
Horizontal Scaling Strategy
Why this matters: A successful parking lot will need to expand. Our design supports:
Adding new floors: Plug-and-play architecture
Increasing capacity: Linear scaling without system redesign
Multi-location support: Centralized management with local processing
Performance Optimization
Load Balancing Strategy:
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ Entry โ โ Entry โ โ Entry โ
โ Gate 1 โ โ Gate 2 โ โ Gate 3 โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Load Balancer โ
โ (Traffic Distribution) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Central Processing โ
โ System โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ ๏ธ Technology Stack Recommendations
Backend Services
Language: Java with Spring Boot for business logic (examples provided above)
Database: PostgreSQL for transactional data, Redis for caching
Message Queue: RabbitMQ for async processing
API Gateway: nginx for load balancing
Framework: Spring Boot with Spring Security for authentication
Hardware Integration
Sensors: Ultrasonic/magnetic for spot detection
Cameras: License plate recognition
Payment Terminals: EMV-compliant card readers
Display Systems: LED matrices with network connectivity
Mobile & Web Interface
Frontend: React Native for cross-platform mobile app
Backend API: RESTful services with real-time WebSocket updates
Admin Dashboard: React.js for management interface
๐ Security & Compliance
Data Protection
PCI DSS Compliance: For payment processing
Data Encryption: All sensitive data encrypted at rest and in transit
Access Control: Role-based permissions for different user types
Physical Security
CCTV Integration: 24/7 monitoring with motion detection
Emergency Systems: Fire safety and evacuation protocols
Access Control: Secure areas for staff and equipment
๐ฏ Implementation Roadmap
Phase 1: Core Functionality (Months 1-3)
โ Basic entry/exit system
โ Ticket generation and validation
โ Payment processing
โ Spot occupancy tracking
Phase 2: Enhanced Features (Months 4-6)
โ Multi-floor support
โ Different vehicle types
โ Real-time display boards
โ Mobile app (basic version)
Phase 3: Advanced Features (Months 7-9)
โ Electric vehicle charging
โ License plate recognition
โ Dynamic pricing
โ Analytics dashboard
Phase 4: AI & Optimization (Months 10-12)
โ Predictive analytics
โ Smart routing algorithms
โ Automated maintenance alerts
โ Customer behavior analysis
๐ Success Metrics
Key Performance Indicators
| Metric | Target | Why It Matters |
| Average Entry Time | < 30 seconds | Customer satisfaction |
| System Uptime | 99.9% | Reliability |
| Payment Success Rate | > 98% | Revenue protection |
| Spot Utilization | > 85% | Efficiency |
| Customer Satisfaction | > 4.5/5 | Retention |
๐ฎ Future Enhancements
Smart City Integration
Traffic Management: Integration with city traffic systems
Environmental Monitoring: Air quality sensors
Energy Management: Solar panels and battery storage
AI-Powered Features
Predictive Maintenance: IoT sensors for proactive repairs
Demand Forecasting: Machine learning for capacity planning
Personalized Experience: Custom recommendations for frequent users
๐ Conclusion
Designing a modern parking lot system is like orchestrating a complex symphonyโevery component must work in perfect harmony. By focusing on modularity, scalability, and user experience, we create a system that not only meets today's needs but adapts to tomorrow's challenges.
The key to success lies in:
๐๏ธ Solid Architecture: Microservices for flexibility
๐ฅ User-Centric Design: Making parking effortless
๐ฎ Future-Ready Technology: Embracing electric vehicles and smart city concepts
๐ Data-Driven Decisions: Analytics for continuous improvement
Remember, a great parking lot system doesn't just store carsโit enhances the entire urban experience. Now, let's build something amazing! ๐
Ready to implement this system? The journey from concept to reality is just as exciting as the destination!
๐ป Implementation Examples
Let's dive into the actual Java code that brings our parking lot system to life! These examples show how to implement the core components with clean, maintainable code.
๐๏ธ Core Domain Models
// Enums for type safety and clarity
public enum VehicleType {
MOTORCYCLE(1, "Motorcycle"),
COMPACT_CAR(2, "Compact Car"),
LARGE_CAR(3, "Large Car"),
TRUCK(4, "Truck"),
ELECTRIC_CAR(2, "Electric Car");
private final int spotsRequired;
private final String displayName;
VehicleType(int spotsRequired, String displayName) {
this.spotsRequired = spotsRequired;
this.displayName = displayName;
}
public int getSpotsRequired() { return spotsRequired; }
public String getDisplayName() { return displayName; }
}
public enum SpotType {
COMPACT(8, 16, VehicleType.COMPACT_CAR, VehicleType.MOTORCYCLE),
LARGE(9, 20, VehicleType.LARGE_CAR, VehicleType.TRUCK, VehicleType.COMPACT_CAR),
HANDICAPPED(11, 20, VehicleType.COMPACT_CAR, VehicleType.LARGE_CAR),
MOTORCYCLE(4, 8, VehicleType.MOTORCYCLE),
ELECTRIC(9, 18, VehicleType.ELECTRIC_CAR);
private final int width;
private final int length;
private final Set<VehicleType> supportedVehicles;
SpotType(int width, int length, VehicleType... supportedVehicles) {
this.width = width;
this.length = length;
this.supportedVehicles = Set.of(supportedVehicles);
}
public boolean canAccommodate(VehicleType vehicleType) {
return supportedVehicles.contains(vehicleType);
}
// Getters...
}
๐ซ Ticket Management System
import java.time.LocalDateTime;
import java.util.UUID;
public class Ticket {
private final String ticketId;
private final LocalDateTime entryTime;
private final VehicleType vehicleType;
private final String spotId;
private LocalDateTime exitTime;
private boolean isPaid;
private BigDecimal amountPaid;
public Ticket(VehicleType vehicleType, String spotId) {
this.ticketId = generateTicketId();
this.entryTime = LocalDateTime.now();
this.vehicleType = vehicleType;
this.spotId = spotId;
this.isPaid = false;
}
private String generateTicketId() {
return "TKT-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
}
public Duration getParkingDuration() {
LocalDateTime endTime = exitTime != null ? exitTime : LocalDateTime.now();
return Duration.between(entryTime, endTime);
}
public void markAsPaid(BigDecimal amount) {
this.amountPaid = amount;
this.isPaid = true;
}
// Getters and setters...
}
@Service
public class TicketService {
private final Map<String, Ticket> activeTickets = new ConcurrentHashMap<>();
private final PricingService pricingService;
public TicketService(PricingService pricingService) {
this.pricingService = pricingService;
}
public Ticket issueTicket(VehicleType vehicleType, String spotId) {
Ticket ticket = new Ticket(vehicleType, spotId);
activeTickets.put(ticket.getTicketId(), ticket);
logger.info("Ticket issued: {} for vehicle type: {} at spot: {}",
ticket.getTicketId(), vehicleType, spotId);
return ticket;
}
public BigDecimal calculateFee(String ticketId) {
Ticket ticket = activeTickets.get(ticketId);
if (ticket == null) {
throw new TicketNotFoundException("Ticket not found: " + ticketId);
}
return pricingService.calculateParkingFee(
ticket.getVehicleType(),
ticket.getParkingDuration()
);
}
public boolean processPayment(String ticketId, BigDecimal amount, PaymentMethod method) {
Ticket ticket = activeTickets.get(ticketId);
BigDecimal requiredAmount = calculateFee(ticketId);
if (amount.compareTo(requiredAmount) >= 0) {
ticket.markAsPaid(amount);
return true;
}
return false;
}
}
๐ ฟ๏ธ Parking Spot Management
public class ParkingSpot {
private final String spotId;
private final SpotType spotType;
private final int floor;
private final String section;
private boolean isOccupied;
private boolean isReserved;
private LocalDateTime lastUpdated;
private String currentTicketId;
public ParkingSpot(String spotId, SpotType spotType, int floor, String section) {
this.spotId = spotId;
this.spotType = spotType;
this.floor = floor;
this.section = section;
this.isOccupied = false;
this.isReserved = false;
this.lastUpdated = LocalDateTime.now();
}
public boolean canAccommodate(VehicleType vehicleType) {
return !isOccupied && !isReserved && spotType.canAccommodate(vehicleType);
}
public synchronized boolean occupy(String ticketId) {
if (canAccommodate(null)) {
this.isOccupied = true;
this.currentTicketId = ticketId;
this.lastUpdated = LocalDateTime.now();
return true;
}
return false;
}
public synchronized void vacate() {
this.isOccupied = false;
this.currentTicketId = null;
this.lastUpdated = LocalDateTime.now();
}
// Getters...
}
@Service
public class ParkingSpotService {
private final Map<String, ParkingSpot> allSpots = new ConcurrentHashMap<>();
private final Map<SpotType, List<ParkingSpot>> spotsByType = new EnumMap<>(SpotType.class);
@PostConstruct
public void initializeSpots() {
// Initialize parking spots for multiple floors
for (int floor = 1; floor <= 3; floor++) {
createSpotsForFloor(floor);
}
}
private void createSpotsForFloor(int floor) {
// Create different types of spots per floor
createSpots(SpotType.COMPACT, floor, "A", 20);
createSpots(SpotType.LARGE, floor, "B", 15);
createSpots(SpotType.HANDICAPPED, floor, "H", 4);
createSpots(SpotType.MOTORCYCLE, floor, "M", 10);
createSpots(SpotType.ELECTRIC, floor, "E", 8);
}
private void createSpots(SpotType type, int floor, String section, int count) {
for (int i = 1; i <= count; i++) {
String spotId = String.format("F%d-%s%02d", floor, section, i);
ParkingSpot spot = new ParkingSpot(spotId, type, floor, section);
allSpots.put(spotId, spot);
spotsByType.computeIfAbsent(type, k -> new ArrayList<>()).add(spot);
}
}
public Optional<ParkingSpot> findAvailableSpot(VehicleType vehicleType) {
return spotsByType.values().stream()
.flatMap(List::stream)
.filter(spot -> spot.canAccommodate(vehicleType))
.findFirst();
}
public Map<SpotType, Long> getAvailabilityByType() {
return spotsByType.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().stream()
.filter(spot -> !spot.isOccupied())
.count()
));
}
public Map<Integer, Map<SpotType, Long>> getAvailabilityByFloor() {
return allSpots.values().stream()
.filter(spot -> !spot.isOccupied())
.collect(Collectors.groupingBy(
ParkingSpot::getFloor,
Collectors.groupingBy(
ParkingSpot::getSpotType,
Collectors.counting()
)
));
}
}
๐ณ Payment Processing System
public interface PaymentProcessor {
PaymentResult processPayment(PaymentRequest request);
boolean refund(String transactionId, BigDecimal amount);
}
@Component
public class CashPaymentProcessor implements PaymentProcessor {
@Override
public PaymentResult processPayment(PaymentRequest request) {
// Simulate cash payment validation
if (request.getAmount().compareTo(BigDecimal.ZERO) > 0) {
return PaymentResult.success(
generateTransactionId(),
request.getAmount(),
"Cash payment processed"
);
}
return PaymentResult.failure("Invalid cash amount");
}
private String generateTransactionId() {
return "CASH-" + System.currentTimeMillis();
}
@Override
public boolean refund(String transactionId, BigDecimal amount) {
// Cash refunds require manual processing
logger.info("Manual cash refund required: {} for amount: {}",
transactionId, amount);
return true;
}
}
@Component
public class CreditCardPaymentProcessor implements PaymentProcessor {
private final PaymentGateway paymentGateway;
public CreditCardPaymentProcessor(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
@Override
public PaymentResult processPayment(PaymentRequest request) {
try {
// Validate card details
if (!isValidCard(request.getCardDetails())) {
return PaymentResult.failure("Invalid card details");
}
// Process through payment gateway
GatewayResponse response = paymentGateway.charge(
request.getCardDetails(),
request.getAmount(),
"Parking Fee - " + request.getTicketId()
);
if (response.isSuccessful()) {
return PaymentResult.success(
response.getTransactionId(),
request.getAmount(),
"Card payment successful"
);
} else {
return PaymentResult.failure(response.getErrorMessage());
}
} catch (PaymentException e) {
logger.error("Payment processing failed", e);
return PaymentResult.failure("Payment processing error");
}
}
private boolean isValidCard(CardDetails cardDetails) {
return cardDetails != null &&
cardDetails.getCardNumber() != null &&
cardDetails.getExpiryDate().isAfter(LocalDate.now()) &&
cardDetails.getCvv() != null;
}
@Override
public boolean refund(String transactionId, BigDecimal amount) {
try {
return paymentGateway.refund(transactionId, amount).isSuccessful();
} catch (PaymentException e) {
logger.error("Refund failed for transaction: {}", transactionId, e);
return false;
}
}
}
@Service
public class PaymentService {
private final Map<PaymentMethod, PaymentProcessor> processors;
private final PaymentRepository paymentRepository;
public PaymentService(List<PaymentProcessor> processors,
PaymentRepository paymentRepository) {
this.processors = processors.stream()
.collect(Collectors.toMap(
this::getPaymentMethod,
Function.identity()
));
this.paymentRepository = paymentRepository;
}
public PaymentResult processPayment(PaymentRequest request) {
PaymentProcessor processor = processors.get(request.getPaymentMethod());
if (processor == null) {
return PaymentResult.failure("Unsupported payment method");
}
PaymentResult result = processor.processPayment(request);
// Save payment record
Payment payment = new Payment(
request.getTicketId(),
request.getAmount(),
request.getPaymentMethod(),
result.getTransactionId(),
result.isSuccessful()
);
paymentRepository.save(payment);
return result;
}
}
โก Electric Vehicle Charging System
public enum ChargingStandard {
TYPE1(120, "Type 1 (J1772)"),
TYPE2(240, "Type 2 (Mennekes)"),
CCS(480, "CCS (Combined Charging System)"),
CHADEMO(480, "CHAdeMO"),
TESLA_SUPERCHARGER(480, "Tesla Supercharger");
private final int maxPower;
private final String description;
ChargingStandard(int maxPower, String description) {
this.maxPower = maxPower;
this.description = description;
}
// Getters...
}
public class ChargingStation {
private final String stationId;
private final String spotId;
private final Set<ChargingStandard> supportedStandards;
private final int maxPowerKw;
private ChargingSession currentSession;
private boolean isOperational;
private LocalDateTime lastMaintenance;
public ChargingStation(String stationId, String spotId,
Set<ChargingStandard> supportedStandards, int maxPowerKw) {
this.stationId = stationId;
this.spotId = spotId;
this.supportedStandards = supportedStandards;
this.maxPowerKw = maxPowerKw;
this.isOperational = true;
this.lastMaintenance = LocalDateTime.now();
}
public boolean startCharging(String ticketId, ChargingStandard standard,
PaymentMethod paymentMethod) {
if (!isAvailable() || !supportedStandards.contains(standard)) {
return false;
}
this.currentSession = new ChargingSession(
ticketId,
standard,
paymentMethod,
LocalDateTime.now()
);
logger.info("Charging started at station {} for ticket {}",
stationId, ticketId);
return true;
}
public ChargingSession stopCharging() {
if (currentSession != null) {
currentSession.endSession();
ChargingSession completedSession = currentSession;
currentSession = null;
logger.info("Charging stopped at station {}. Duration: {} minutes, Energy: {} kWh",
stationId,
completedSession.getDurationMinutes(),
completedSession.getEnergyDelivered());
return completedSession;
}
return null;
}
public boolean isAvailable() {
return isOperational && currentSession == null;
}
public ChargingStatus getStatus() {
if (!isOperational) {
return ChargingStatus.OUT_OF_ORDER;
}
if (currentSession == null) {
return ChargingStatus.AVAILABLE;
}
return ChargingStatus.CHARGING;
}
}
public class ChargingSession {
private final String ticketId;
private final ChargingStandard standard;
private final PaymentMethod paymentMethod;
private final LocalDateTime startTime;
private LocalDateTime endTime;
private BigDecimal energyDelivered; // kWh
private BigDecimal cost;
public ChargingSession(String ticketId, ChargingStandard standard,
PaymentMethod paymentMethod, LocalDateTime startTime) {
this.ticketId = ticketId;
this.standard = standard;
this.paymentMethod = paymentMethod;
this.startTime = startTime;
this.energyDelivered = BigDecimal.ZERO;
}
public void endSession() {
this.endTime = LocalDateTime.now();
this.cost = calculateChargingCost();
}
private BigDecimal calculateChargingCost() {
// $0.30 per kWh base rate
BigDecimal ratePerKwh = new BigDecimal("0.30");
return energyDelivered.multiply(ratePerKwh);
}
public long getDurationMinutes() {
LocalDateTime end = endTime != null ? endTime : LocalDateTime.now();
return Duration.between(startTime, end).toMinutes();
}
// Getters and setters...
}
@Service
public class ChargingStationService {
private final Map<String, ChargingStation> stations = new ConcurrentHashMap<>();
private final PaymentService paymentService;
public ChargingStationService(PaymentService paymentService) {
this.paymentService = paymentService;
initializeChargingStations();
}
private void initializeChargingStations() {
// Initialize charging stations for electric spots
for (int floor = 1; floor <= 3; floor++) {
for (int i = 1; i <= 8; i++) {
String spotId = String.format("F%d-E%02d", floor, i);
String stationId = "CHG-" + spotId;
Set<ChargingStandard> standards = Set.of(
ChargingStandard.TYPE2,
ChargingStandard.CCS
);
ChargingStation station = new ChargingStation(
stationId, spotId, standards, 50
);
stations.put(stationId, station);
}
}
}
public Optional<ChargingStation> findAvailableStation() {
return stations.values().stream()
.filter(ChargingStation::isAvailable)
.findFirst();
}
public boolean startCharging(String stationId, String ticketId,
ChargingStandard standard, PaymentMethod paymentMethod) {
ChargingStation station = stations.get(stationId);
if (station == null) {
return false;
}
return station.startCharging(ticketId, standard, paymentMethod);
}
public BigDecimal stopChargingAndCalculateCost(String stationId) {
ChargingStation station = stations.get(stationId);
if (station == null) {
return BigDecimal.ZERO;
}
ChargingSession session = station.stopCharging();
return session != null ? session.getCost() : BigDecimal.ZERO;
}
}
๐ Real-Time Display System
@Component
public class DisplayBoardService {
private final ParkingSpotService parkingSpotService;
private final ChargingStationService chargingStationService;
@Scheduled(fixedRate = 5000) // Update every 5 seconds
public void updateDisplayBoards() {
DisplayData displayData = generateDisplayData();
broadcastToDisplays(displayData);
}
private DisplayData generateDisplayData() {
Map<Integer, Map<SpotType, Long>> availabilityByFloor =
parkingSpotService.getAvailabilityByFloor();
long availableChargingStations = chargingStationService
.getAllStations()
.stream()
.mapToLong(station -> station.isAvailable() ? 1 : 0)
.sum();
return new DisplayData(
availabilityByFloor,
availableChargingStations,
LocalDateTime.now()
);
}
private void broadcastToDisplays(DisplayData data) {
// Send to entry displays
displayControllerService.updateEntryDisplays(data);
// Send to floor displays
for (int floor = 1; floor <= 3; floor++) {
displayControllerService.updateFloorDisplay(floor, data);
}
// Send to mobile app via WebSocket
webSocketService.broadcastAvailability(data);
}
}
public class DisplayData {
private final Map<Integer, Map<SpotType, Long>> availabilityByFloor;
private final long availableChargingStations;
private final LocalDateTime lastUpdate;
public DisplayData(Map<Integer, Map<SpotType, Long>> availabilityByFloor,
long availableChargingStations, LocalDateTime lastUpdate) {
this.availabilityByFloor = availabilityByFloor;
this.availableChargingStations = availableChargingStations;
this.lastUpdate = lastUpdate;
}
public String generateDisplayText() {
StringBuilder display = new StringBuilder();
display.append("=== PARKING AVAILABILITY ===\n");
availabilityByFloor.forEach((floor, spots) -> {
display.append(String.format("FLOOR %d: ", floor));
spots.forEach((type, count) -> {
String emoji = getEmojiForSpotType(type);
display.append(String.format("%s %d ", emoji, count));
});
display.append("\n");
});
display.append(String.format("โก CHARGING: %d AVAILABLE\n", availableChargingStations));
display.append(String.format("Updated: %s",
lastUpdate.format(DateTimeFormatter.ofPattern("HH:mm:ss"))));
return display.toString();
}
private String getEmojiForSpotType(SpotType type) {
return switch (type) {
case COMPACT -> "๐";
case LARGE -> "๐";
case HANDICAPPED -> "โฟ";
case MOTORCYCLE -> "๐๏ธ";
case ELECTRIC -> "โก";
};
}
// Getters...
}
๐ฏ Main Parking Lot Controller
@RestController
@RequestMapping("/api/parking")
public class ParkingLotController {
private final TicketService ticketService;
private final ParkingSpotService parkingSpotService;
private final PaymentService paymentService;
private final ChargingStationService chargingStationService;
@PostMapping("/entry")
public ResponseEntity<EntryResponse> enterParkingLot(
@RequestBody EntryRequest request) {
try {
// Find available spot
Optional<ParkingSpot> availableSpot =
parkingSpotService.findAvailableSpot(request.getVehicleType());
if (availableSpot.isEmpty()) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(new EntryResponse("No available spots", null));
}
ParkingSpot spot = availableSpot.get();
// Issue ticket
Ticket ticket = ticketService.issueTicket(
request.getVehicleType(),
spot.getSpotId()
);
// Occupy spot
if (spot.occupy(ticket.getTicketId())) {
// For electric vehicles, also reserve charging station
if (request.getVehicleType() == VehicleType.ELECTRIC_CAR) {
Optional<ChargingStation> station =
chargingStationService.findAvailableStation();
if (station.isPresent()) {
ticket.setChargingStationId(station.get().getStationId());
}
}
return ResponseEntity.ok(new EntryResponse(
"Welcome! Proceed to " + spot.getSpotId(),
ticket
));
} else {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(new EntryResponse("Spot assignment failed", null));
}
} catch (Exception e) {
logger.error("Entry processing failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new EntryResponse("System error", null));
}
}
@PostMapping("/payment")
public ResponseEntity<PaymentResponse> processPayment(
@RequestBody PaymentRequest request) {
try {
BigDecimal fee = ticketService.calculateFee(request.getTicketId());
request.setAmount(fee);
PaymentResult result = paymentService.processPayment(request);
if (result.isSuccessful()) {
return ResponseEntity.ok(new PaymentResponse(
"Payment successful",
result.getTransactionId(),
fee
));
} else {
return ResponseEntity.badRequest()
.body(new PaymentResponse(result.getErrorMessage(), null, fee));
}
} catch (TicketNotFoundException e) {
return ResponseEntity.notFound().build();
} catch (Exception e) {
logger.error("Payment processing failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new PaymentResponse("Payment system error", null, null));
}
}
@PostMapping("/exit")
public ResponseEntity<ExitResponse> exitParkingLot(
@RequestBody ExitRequest request) {
try {
Ticket ticket = ticketService.getTicket(request.getTicketId());
if (!ticket.isPaid()) {
return ResponseEntity.badRequest()
.body(new ExitResponse("Payment required before exit", false));
}
// Free up the parking spot
ParkingSpot spot = parkingSpotService.getSpot(ticket.getSpotId());
spot.vacate();
// Stop charging if applicable
if (ticket.getChargingStationId() != null) {
BigDecimal chargingCost = chargingStationService
.stopChargingAndCalculateCost(ticket.getChargingStationId());
if (chargingCost.compareTo(BigDecimal.ZERO) > 0) {
// Additional charging payment may be required
// This could be handled separately or added to parking fee
}
}
// Mark ticket as used
ticketService.completeTicket(request.getTicketId());
return ResponseEntity.ok(new ExitResponse(
"Thank you for parking with us! Have a great day!",
true
));
} catch (Exception e) {
logger.error("Exit processing failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ExitResponse("Exit system error", false));
}
}
@GetMapping("/availability")
public ResponseEntity<Map<SpotType, Long>> getAvailability() {
return ResponseEntity.ok(parkingSpotService.getAvailabilityByType());
}
@GetMapping("/status/{ticketId}")
public ResponseEntity<TicketStatus> getTicketStatus(@PathVariable String ticketId) {
try {
Ticket ticket = ticketService.getTicket(ticketId);
BigDecimal currentFee = ticketService.calculateFee(ticketId);
return ResponseEntity.ok(new TicketStatus(
ticket.getTicketId(),
ticket.getSpotId(),
ticket.getParkingDuration(),
currentFee,
ticket.isPaid()
));
} catch (TicketNotFoundException e) {
return ResponseEntity.notFound().build();
}
}
}
These examples demonstrate:
โ
Clean Architecture - Separation of concerns with services, controllers, and domain models
โ
Type Safety - Using enums and strong typing throughout
โ
Concurrency - Thread-safe operations with proper synchronization
โ
Error Handling - Comprehensive exception handling and logging
โ
Scalability - Modular design that can be easily extended
โ
Real-world Patterns - Repository pattern, dependency injection, and RESTful APIs
The code shows how to implement all the key features we discussed, from basic parking operations to advanced features like EV charging integration!




