List the key functional requirements for the system (Ask the AI for hints if stuck)...
List the key non-functional requirements (performance, scalability, reliability, etc.)...
Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
read/write ration - 2:1
10 countries ~ 100 lots each = 1000 lots, 200 reservations/day per lot = 200k reservation/day globally.
1 vehicle will have 128b, ~25 mb data per day
moving the data to cold storage (archive) monthly
Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
GET /parking/available - This uses optimized space allocation strategy.
POST /parking/lots/assign - This puts a lock on the space.
POST /parking/lots/remove - This frees up the space.
GET /parking/payment - Calculate charges.
POST /parking/confirm - confirms slot
POST /parking/ticket - Generate ticket.
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
Load Balancer - Manages load and scale up service based on load.
API Gateway - This manages rate limiting/throttling, authentication/authorization (JWT/Oauth).
Parking Display Service - Tracks real-time spot availability, slot allocation by size/type (compact, large, disabled), and updates display boards.
Payment Service - An internal business logic service that computes charges based on entry-exit timestamps and vehicle categories. Uses third party app for payment. On successul payment, it frees up the parking lot. The communication is async webhook based.
User Service - Manage all user related operations.
Ticketing Service - Creates Ticket and handles entry/exit operations, issues tickets,
User searches availability (Parking Display Service with Redis cache) → reserves a spot → Payment Service processes payment via third-party PSP → Ticketing Service generates ticket on confirmation.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
We will use PostgresQL db.
CREATE TABLE parking_lots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
address TEXT NOT NULL,
total_capacity INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE floors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lot_id UUID REFERENCES parking_lots(id) ON DELETE CASCADE,
floor_number INT NOT NULL,
max_height_meters DECIMAL(4,2),
UNIQUE(lot_id, floor_number)
);
CREATE TYPE spot_type AS ENUM ('compact', 'large', 'ev_charging', 'handicapped');
CREATE TABLE parking_spots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
floor_id UUID REFERENCES floors(id) ON DELETE CASCADE,
spot_number VARCHAR(10) NOT NULL,
type spot_type DEFAULT 'compact',
is_operational BOOLEAN DEFAULT TRUE,
UNIQUE(floor_id, spot_number)
);
CREATE TYPE ticket_status AS ENUM ('active', 'paid', 'completed', 'disputed');
CREATE TABLE parking_tickets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lot_id UUID REFERENCES parking_lots(id),
spot_id UUID REFERENCES parking_spots(id),
vehicle_id UUID REFERENCES vehicles(id),
license_plate_captured VARCHAR(20) NOT NULL, -- Fallback for non-registered users
entry_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
exit_time TIMESTAMP,
status ticket_status DEFAULT 'active',
total_amount DECIMAL(10,2) DEFAULT 0.00
);
CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ticket_id UUID REFERENCES parking_tickets(id) ON DELETE RESTRICT,
amount DECIMAL(10,2) NOT NULL,
payment_method VARCHAR(30) NOT NULL, -- 'credit_card', 'upi', 'cash', 'app_wallet'
transaction_reference VARCHAR(100) UNIQUE,
paid_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), full_name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, phone VARCHAR(20), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE vehicles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, license_plate VARCHAR(20) UNIQUE NOT NULL, color VARCHAR(20), model VARCHAR(50) ); -- 3. TARIFFS & RATE CARDSCREATE TABLE rate_cards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lot_id UUID REFERENCES parking_lots(id) ON DELETE CASCADE, spot_type spot_type NOT NULL, base_rate_first_hour DECIMAL(10,2) NOT NULL, hourly_rate_after DECIMAL(10,2) NOT NULL, daily_max_rate DECIMAL(10,2) NOT NULL, effective_from TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
We can use a Horizontal Sharding architecture managed by application routing logic or an extension like Citus
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
Ticketing service - Before assigning a ticket to a vehicle, Ticketing service first checks the allocatable spot for this vehicle based on size to optimize space. It then notes down timestamp and vehicle details and locks that spot on DB level using optimistic locking.
At this point, if any other vehicle checks for the same spot, it will be marked as occupied and will not be assigned to anyone else.
When the vehicle exits, it will release the lock and make that spot available.
Anytime there is a spot assigned, we clear the cache entry for that spot.
Every lookup first goes to cache and then to the DB and all DB commit remove the stale entry present in the cache.
This interaction between DB and Redis happen via kafka/sqs. For strong consistency, we must bypass asynchronous brokers for the cache eviction and use a synchronous Cache-Aside Write pattern directly inside the API request cycle.
Hold expiration — We run a cron job and if the hold time exceeds that, we mark that spot as available again.
To handle duplicate payment or reservation requests safely, we will implement idempotency.
Payment Service uses an atomic cache (like Redis) as a central tracking registry.
like - Check Redis for Key -> Atomic Write to Redis: SET key "PENDING" NX EX 86400 -> Execute Authoritative DB Transaction / Charge Gateway -> Update Redis: SET key "DONE:{\"http_code\":200,...}" -> Return Response to Client