Requirements
Functional Requirements:
- Allow reservation of a parking spot.
- Process payment for the reservation.
- payment model (monthly, daily, yearly) or support cash or mobile pay.
- Enable parking of a car in the reserved spot.
- Support early departure before reservation time expires.
- Gate check-in/out.(type of checking license or tickets or mobile app )
- Handle no show.
- real-time availability display
- vehicles supported
- shows strict if it is reserved for whole day
Non-Functional Requirements:
- slow latency
- high performance
- fault tolerance
- race condition/concurrency ( keep in mind that multiple cars might reserve at the same time)
- scalability
API Design
# 1. Check availability (Real-time)
GET /api/v1/lots/{lotId}/availability?vehicleType=CAR&start=2026-04-01T10:00:00Z&end=2026-04-01T14:00:00Z
Response: {
"availableSpots": 12,
"spots": [{"id": "A-101", "type": "COMPACT", "price": 15.00}],
"dynamicPricing": {"surgeMultiplier": 1.2}
}
# 2. Hold spot (Pre-reservation lock)
POST /api/v1/reservations/hold
Body: {
"lotId": "lot-123",
"spotId": "A-101",
"start": "...", "end": "..."}
Response: {
"holdId": "hold-uuid-789",
"expiresAt": "2026-04-01T09:15:00Z", # 15 min to pay
"paymentIntent": "pi_123456"
}
# 3. Confirm reservation (After payment)
POST /api/v1/reservations/confirm
Body: {"holdId": "hold-uuid-789", "paymentMethod": "card_123"}
Response: {
"reservationId": "res-456",
"qrCode": "base64encoded",
"licensePlate": "ABC-123"
}
# Gate checks if vehicle can enter
POST /api/v1/gate/validate
Body: {
"gateId": "gate-south-1",
"licensePlate": "ABC-123", # OR "qrCode": "..."
"timestamp": "2026-04-01T09:58:00Z"
}
Response: {
"allowed": true,
"reservationId": "res-456",
"spotId": "A-101",
"action": "OPEN_GATE"
}
High-Level Design
database design :
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Lots │ │ ParkingSpots │ │ Vehicles │
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
│ lot_id (PK) │◄──────│ spot_id (PK) │ │ vehicle_id (PK) │
│ name │ │ lot_id (FK) │ │ license_plate │
│ timezone │ │ spot_number │ │ type │
│ location_geo │ │ type │ │ user_id (FK) │
│ config_json │ │ status │ └────────┬────────┘
└─────────────────┘ │ hardware_sensor_id │
└─────────┬────────┘ │
│ │
┌─────────────────┐ │ │
│ Reservations │─────────────────┘ │
├─────────────────┤ (reserves spot) │
│ reservation_id │ │
│ spot_id (FK) │ │
│ vehicle_id (FK) │◄─────────────────────────────────┘
│ user_id (FK) │
│ time_range │ ┌──────────────────┐
│ status │ │ Payments │
│ payment_id (FK) │──────►│ payment_id (PK) │
└─────────────────┘ │ reservation_id │
│ type/subscription│
│ status │
└──────────────────┘
Detailed Component Design
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
DB design:
CREATE TABLE lots (
lot_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
address TEXT NOT NULL,
timezone VARCHAR(50) NOT NULL DEFAULT 'UTC',
-- Geospatial for "find nearby" feature
location GEOGRAPHY(POINT, 4326) NOT NULL,
geo_fence GEOGRAPHY(POLYGON, 4326), -- For mobile app auto-check-in
-- Configuration
max_height_cm INTEGER, -- For "will my truck fit?"
is_active BOOLEAN DEFAULT true,
config JSONB NOT NULL DEFAULT '{
"gate_type": "automated",
"entry_lead_time_minutes": 15,
"grace_period_minutes": 15,
"overstay_rate_per_hour": 10.00
}',
-- Operational hours (ISO 8601 format or custom)
operating_hours JSONB DEFAULT '{
"monday": {"open": "00:00", "close": "23:59"},
"tuesday": {"open": "00:00", "close": "23:59"}
}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT valid_lot CHECK (name <> '')
);
-- Indexes for geospatial queries (find lots near user)
CREATE INDEX idx_lots_location ON lots USING GIST(location);
CREATE INDEX idx_lots_active ON lots(lot_id) WHERE is_active = true;
CREATE TYPE spot_type AS ENUM (
'COMPACT', -- Small cars only
'REGULAR', -- Standard sedan/SUV
'LARGE', -- Trucks, vans
'HANDICAP', -- Accessibility required
'ELECTRIC', -- EV charging available
'MOTORCYCLE', -- Bike spots (multiple per physical space?)
'BUS', -- Multiple contiguous spots
'VALET_ONLY' -- No self-parking
);
CREATE TYPE spot_status AS ENUM (
'AVAILABLE', -- Free for reservation or drive-up
'OCCUPIED', -- Physically has vehicle
'RESERVED', -- Held for upcoming reservation
'MAINTENANCE', -- Out of order
'BLOCKED' -- Admin blocked (event, cleaning)
);
CREATE TABLE parking_spots (
spot_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lot_id UUID NOT NULL REFERENCES lots(lot_id) ON DELETE CASCADE,
-- Physical identification
floor_number INTEGER DEFAULT 1,
section_code VARCHAR(10), -- "A", "B", "North", etc.
spot_number VARCHAR(20) NOT NULL, -- "A-101", "P2-45"
-- Physical characteristics
spot_type spot_type NOT NULL DEFAULT 'REGULAR',
width_cm INTEGER,
length_cm INTEGER,
-- Real-time state (cached in Redis, persisted here)
current_status spot_status DEFAULT 'AVAILABLE',
current_vehicle_id UUID REFERENCES vehicles(vehicle_id),
sensor_last_seen TIMESTAMP WITH TIME ZONE, -- IoT heartbeat
-- Hardware integration
sensor_id VARCHAR(50), -- IoT device ID
gate_id VARCHAR(50), -- Which gate controls access
-- Constraints
is_active BOOLEAN DEFAULT true,
-- Prevent duplicate spot numbers per lot
UNIQUE(lot_id, floor_number, section_code, spot_number),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Critical indexes for availability queries
CREATE INDEX idx_spots_lot_status ON parking_spots(lot_id, spot_type, current_status)
WHERE is_active = true;
CREATE INDEX idx_spots_sensor ON parking_spots(sensor_id) WHERE sensor_id IS NOT NULL;
CREATE TABLE users (
user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(20),
-- Payment defaults
default_payment_method_id VARCHAR(50),
-- Reputation/scoring (for dynamic pricing)
no_show_count INTEGER DEFAULT 0,
trust_score INTEGER DEFAULT 100, -- 0-100
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE vehicles (
vehicle_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
license_plate VARCHAR(20) NOT NULL,
state_province VARCHAR(50),
country VARCHAR(2) DEFAULT 'US',
vehicle_type spot_type NOT NULL DEFAULT 'REGULAR',
color VARCHAR(30),
make_model VARCHAR(100),
-- For license plate recognition confidence
plate_image_url TEXT,
-- Unique per user (one user can't add same plate twice)
UNIQUE(user_id, license_plate),
-- But different users can have same plate (rental cars, family sharing)
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Lookup for gate LPR (License Plate Recognition)
CREATE INDEX idx_vehicles_plate ON vehicles(license_plate, state_province);
CREATE TYPE reservation_status AS ENUM (
'PENDING', -- Initial hold, not paid yet (15-min TTL)
'CONFIRMED', -- Paid, waiting for start time
'ACTIVE', -- Vehicle entered, currently parked
'COMPLETED', -- Exited normally
'NO_SHOW', -- Never arrived
'CANCELLED', -- User cancelled
'EXPIRED' -- Hold expired without payment
);
CREATE TYPE payment_model AS ENUM (
'HOURLY', -- Pay for actual duration
'DAILY', -- Flat daily rate
'MONTHLY_PASS', -- Subscription covers this
'YEARLY_PASS', -- Subscription covers this
'VALIDATED', -- 3rd party validation (employer, event)
'FREE' -- Comp/promo
);
CREATE TABLE reservations (
reservation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Relationships
spot_id UUID NOT NULL REFERENCES parking_spots(spot_id),
user_id UUID NOT NULL REFERENCES users(user_id),
vehicle_id UUID NOT NULL REFERENCES vehicles(vehicle_id),
-- Time boundaries (with timezone!)
scheduled_start TIMESTAMP WITH TIME ZONE NOT NULL,
scheduled_end TIMESTAMP WITH TIME ZONE NOT NULL,
-- Actual times (for analytics & billing adjustments)
actual_entry TIMESTAMP WITH TIME ZONE,
actual_exit TIMESTAMP WITH TIME ZONE,
-- Status tracking
status reservation_status NOT NULL DEFAULT 'PENDING',
-- Payment configuration
payment_model payment_model NOT NULL DEFAULT 'HOURLY',
subscription_id UUID REFERENCES subscriptions(subscription_id),
-- Pricing snapshot (prices can change, but reservation locks them)
base_rate_per_hour DECIMAL(10,2),
total_paid DECIMAL(10,2) DEFAULT 0.00,
refund_amount DECIMAL(10,2) DEFAULT 0.00,
-- Early departure handling
early_exit_eligible BOOLEAN DEFAULT false,
early_exit_refund_processed BOOLEAN DEFAULT false,
-- No-show tracking
no_show_charge_processed BOOLEAN DEFAULT false,
-- Concurrency control (Optimistic Locking)
version INTEGER NOT NULL DEFAULT 1,
-- Soft delete for audit trails
deleted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Constraints
CONSTRAINT valid_time_range CHECK (scheduled_end > scheduled_start),
CONSTRAINT future_reservation CHECK (scheduled_start > created_at - INTERVAL '1 hour'),
-- Prevent double booking at database level!
CONSTRAINT no_double_booking EXCLUDE USING GIST (
spot_id WITH =,
tsrange(scheduled_start, scheduled_end) WITH &&
)
);
-- Critical indexes
CREATE INDEX idx_reservations_user ON reservations(user_id, scheduled_start DESC);
CREATE INDEX idx_reservations_spot ON reservations(spot_id, scheduled_start DESC);
CREATE INDEX idx_reservations_active ON reservations(vehicle_id, status)
WHERE status IN ('CONFIRMED', 'ACTIVE');
CREATE INDEX idx_reservations_pending_cleanup ON reservations(status, created_at)
WHERE status = 'PENDING'; -- For cleanup cron
-- For finding reservations by license plate at gate (LPR lookup)
CREATE INDEX idx_reservations_vehicle_time ON reservations(vehicle_id, scheduled_start, scheduled_end);