How to design parking lot system
Let's design a Parking Lot System in Node.js. We'll break it down into the main components and provide an implementation for each.
Requirements Recap
Functional Requirements
- Parking Spots Management:
- Different types of parking spots (e.g., regular, compact, handicapped, electric).
- Vehicle Management:
- Vehicles can be parked and removed.
- Different types of vehicles (e.g., motorcycles, cars, trucks).
- Parking Fee Calculation:
- Calculate parking fees based on time and type of vehicle.
- Entry/Exit Points:
- Handle vehicle entry and exit.
- Availability Check:
- Check for available parking spots.
Non-Functional Requirements
- Scalability:
- Handle a large number of vehicles and parking spots.
- Reliability:
- Ensure the system is always available.
- Performance:
- Efficiently handle the entry and exit of vehicles.
Node.js Implementation
We'll use Express for the web framework and a simple in-memory data structure to manage the parking lot state.
1. Project Setup
First, set up a new Node.js project:
mkdir parking-lot-system
cd parking-lot-system
npm init -y
npm install express
2. Basic Structure
Create the following files:
app.js
: Main entry pointmodels.js
: Contains classes forParkingLot
,ParkingSpot
,Vehicle
, andTicket
routes.js
: Defines routes for the API
3. Models (models.js
)
Define the classes for ParkingLot
, ParkingSpot
, Vehicle
, and Ticket
:
// models.js
const { v4: uuidv4 } = require('uuid');
class ParkingSpot {
constructor(id, spotType) {
this.id = id;
this.spotType = spotType;
this.isAvailable = true;
this.vehicle = null;
}
assignVehicle(vehicle) {
this.isAvailable = false;
this.vehicle = vehicle;
}
removeVehicle() {
this.isAvailable = true;
this.vehicle = null;
}
}
class Vehicle {
constructor(licensePlate, vehicleType) {
this.licensePlate = licensePlate;
this.vehicleType = vehicleType;
}
}
class Ticket {
constructor(vehicle, entryTime) {
this.id = uuidv4();
this.vehicle = vehicle;
this.entryTime = entryTime;
this.exitTime = null;
this.fee = 0;
}
setExitTime(exitTime) {
this.exitTime = exitTime;
this.calculateFee();
}
calculateFee() {
const parkedHours = Math.ceil((this.exitTime - this.entryTime) / (1000 * 60 * 60));
this.fee = parkedHours * 10; // Example fee rate: $10 per hour
}
}
class ParkingLot {
constructor() {
this.spots = [];
this.activeTickets = new Map();
}
createParkingSpots(numSpots) {
for (let i = 0; i < numSpots; i++) {
let spotType;
if (i % 4 === 0) {
spotType = 'REGULAR';
} else if (i % 4 === 1) {
spotType = 'COMPACT';
} else if (i % 4 === 2) {
spotType = 'HANDICAPPED';
} else {
spotType = 'ELECTRIC';
}
this.spots.push(new ParkingSpot(i, spotType));
}
}
findAvailableSpot(vehicleType) {
return this.spots.find(spot => spot.isAvailable && (
(vehicleType === 'MOTORCYCLE' && ['REGULAR', 'COMPACT', 'HANDICAPPED', 'ELECTRIC'].includes(spot.spotType)) ||
(vehicleType === 'CAR' && ['REGULAR', 'HANDICAPPED', 'ELECTRIC'].includes(spot.spotType)) ||
(vehicleType === 'TRUCK' && spot.spotType === 'REGULAR')
));
}
parkVehicle(vehicle) {
const spot = this.findAvailableSpot(vehicle.vehicleType);
if (spot) {
spot.assignVehicle(vehicle);
const ticket = new Ticket(vehicle, new Date());
this.activeTickets.set(vehicle.licensePlate, ticket);
return ticket;
}
return null;
}
removeVehicle(licensePlate) {
const ticket = this.activeTickets.get(licensePlate);
if (ticket) {
const spot = this.spots.find(spot => spot.vehicle && spot.vehicle.licensePlate === licensePlate);
spot.removeVehicle();
ticket.setExitTime(new Date());
this.activeTickets.delete(licensePlate);
return ticket;
}
return null;
}
}
module.exports = { ParkingLot, Vehicle };
4. Routes (routes.js
)
Define the routes for parking and removing vehicles:
// routes.js
const express = require('express');
const router = express.Router();
const { ParkingLot, Vehicle } = require('./models');
const parkingLot = new ParkingLot();
parkingLot.createParkingSpots(100);
router.post('/park', (req, res) => {
const { licensePlate, vehicleType } = req.body;
const vehicle = new Vehicle(licensePlate, vehicleType);
const ticket = parkingLot.parkVehicle(vehicle);
if (ticket) {
res.json(ticket);
} else {
res.status(400).json({ error: 'No available spot for the vehicle' });
}
});
router.post('/remove', (req, res) => {
const { licensePlate } = req.body;
const ticket = parkingLot.removeVehicle(licensePlate);
if (ticket) {
res.json(ticket);
} else {
res.status(400).json({ error: 'Ticket not found' });
}
});
module.exports = router;
5. Main Application (app.js
)
Set up the Express server and include the routes:
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const parkingRoutes = require('./routes');
const app = express();
const PORT = 3000;
app.use(bodyParser.json());
app.use('/parking', parkingRoutes);
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Running the Application
Start the application:
node app.js
Testing the API
You can use a tool like Postman or curl to test the endpoints:
Park a Vehicle
curl -X POST http://localhost:3000/parking/park -H "Content-Type: application/json" -d '{"licensePlate": "ABC123", "vehicleType": "CAR"}'
Remove a Vehicle
curl -X POST http://localhost:3000/parking/remove -H "Content-Type: application/json" -d '{"licensePlate": "ABC123"}'
Considerations
-
Persistence:
- For a production system, use a database to persist data instead of in-memory storage.
-
Concurrency:
- Use proper locking mechanisms if multiple instances are running to avoid race conditions.
-
Scalability:
- Implement a distributed system with microservices if the parking lot needs to scale.
This basic implementation demonstrates the core functionalities of a Parking Lot System in Node.js, with extensibility for more complex features as needed.
Published on: Jul 10, 2024, 12:04 AM