Room data: assuming 1k of metadata for each room, total data is 100k * 1k = 100Mb
Request volume:
// Manage room inventory
POST /rooms -> Room
{
id, //output
name,
buildingId,
floor,
address,
lat, // output
long, // output
capacity,
videoLink,
status, // output
...
}
GET /rooms/ -> Room
DELETE / PATCH
// Find rooms
GET /rooms?lat={}&long={}&buildiingId={}&floor={}&capacity={}&time_start={}&time_end={}& -> Room[]
// Reserve room
POST /reservations -> Reservation
{
id, // output
owner, // output. Owner is the authenticated user
participants,
timeStart,
timeEnd,
}
// Manage reservations
GET /reservations/ -> Reservation
PATCH / DELETE
// User table is omitted
// Building table is omitted
Room table (100Mb)
Reservations
We will use a relational database for its strong consistency.
Reservations: per day 2M *100b = 200Mb. Yearly growth is 200Mb * 365 = 70Gb.
Both tables are very small and can be hosted on a single instance of PostgreSql.
In addition, since the room table is very small, we enable full text index on name and address, and geo index on lat & long. Floor, capacity and status are also indexed.
For reservation table, we index timeStart and timeEnd.
Gateway sits in between client and other services to act as load balancer, SSL termination, authentication, rate limiter, etc.
Room service handles room management and search requests.
Reservation service handles reservations.
Both services talk to the tables int the same database.
Client
Gateway
Room Service
Reservation Service
Database
Job queue
Job Service
Room management (using room creation as an example):
For an update or delete request, reservations may need to be changed.
For a room search request
Reservation flow:
Reservation:
Caching
Cleanup:
Database:
Room service, reservation service and job service are stateless and can be horizontally scaled for high availability.
For job queue, we can choose a managed queue (like SQS) or Kafka. The message volume is not high so we probably don't need extra partitioning.
For the database, we should enable high availability mode (having standbys with replication, read replicas)
Traffic spike:
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...
1) To book a room for a given time period:
POST booking.com/v1/room/book
input:
roomId
userDetails
timePeriod
output: booking confirmation with a booking ID upon success
2) To view availability of room(s) for a given time period
GET booking.com/v1/room/availability?startTime=20250404103000&endTime=20250404110000
output:
boolean: true/false (or array of booleans for multiple rooms)
3) To view bookings of room(s) for a given time period
GET booking.com/v1/room/bookings?startTime=t1&endTime=t2
output:
array of bookings
each booking has:
startTime
endTime
DELETE booking.com/v1/room/book/{bookingId}
GET booking.com/v1/user/profile/{userId}
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.
1st option is preferred for cases where we need to support lot of concurrent operations with low latency.
2nd option is preferred for simplicity and comparatively less load.
Depending on our use case we can choose one.
[Client Apps] → [CDN] → [API Gateway] → [Load Balancer]
↓
┌─────────────────┴─────────────────┐
↓ ↓
[Service Cluster] [Cache Layer]
↓ ↓
┌───────────────┼───────────────┐ [Redis Cluster]
↓ ↓ ↓
[Booking Service] [User Service] [Availability Service]
↓ ↓ ↓
[Notification Queue] [Search Service] [Admin Service]
↓
[Notification Workers]
↓
[Email/SMS Providers]
[Primary DB] ← [Replica DB1] ← [Replica DB2]
4. Core Components
4.1 API Gateway
Purpose: Single entry point, routing, authentication, rate limiting
Tech: Kong, AWS API Gateway, or custom Nginx
Features:
JWT token validation
Request throttling (100 req/min per user)
API versioning
4.2 Load Balancer
Purpose: Distribute traffic across service instances
Tech: HAProxy, AWS ALB, Nginx
Strategy: Round-robin with health checks
Sticky sessions: For stateful operations
4.3 Microservices
Booking Service
Responsibilities: Create, update, cancel bookings
API Endpoints:
POST /bookings - Create booking
GET /bookings/{id} - Get booking details
PUT /bookings/{id} - Modify booking
DELETE /bookings/{id} - Cancel booking
Key Logic:
Distributed locking for double-booking prevention
Transaction management
Conflict resolution
Availability Service
Responsibilities: Check room availability, time slot management
API Endpoints:
GET /rooms/available?startTime=X&endTime=Y&capacity=Z
GET /rooms/{id}/slots?date=X
Optimization: Heavy caching, read replicas
User Service
Responsibilities: Authentication, authorization, profile management
API Endpoints:
POST /auth/login
POST /auth/register
GET /users/{id}
PUT /users/{id}
Notification Service
Responsibilities: Send booking confirmations, reminders, cancellations
Pattern: Asynchronous message queue
Tech: Kafka, RabbitMQ, AWS SQS
Search Service
Responsibilities: Fast full-text search on rooms
Tech: Elasticsearch, Algolia
Features: Filter by location, capacity, amenities, price
4.4 Caching Layer
Redis Cluster for:
Room availability (TTL: 5 minutes)
User sessions (TTL: 24 hours)
Frequent queries (popular rooms, time slots)
Distributed locks (prevent double-booking)
Cache Strategy:
Read-through: Check cache first, then DB
Write-through: Update cache on booking changes
Cache invalidation: On booking/cancellation
4.5 Database Design
Primary Database: PostgreSQL (ACID compliance critical)
Schema:
sql
-- Users table
users (
user_id UUID PRIMARY KEY,
email VARCHAR UNIQUE,
name VARCHAR,
phone VARCHAR,
created_at TIMESTAMP
)
-- Rooms table
rooms (
room_id UUID PRIMARY KEY,
name VARCHAR,
location VARCHAR,
capacity INT,
amenities JSONB,
hourly_rate DECIMAL,
created_at TIMESTAMP
)
-- Bookings table
bookings (
booking_id UUID PRIMARY KEY,
room_id UUID REFERENCES rooms(room_id),
user_id UUID REFERENCES users(user_id),
start_time TIMESTAMP,
end_time TIMESTAMP,
status ENUM('confirmed', 'cancelled', 'completed'),
created_at TIMESTAMP,
CONSTRAINT no_overlap UNIQUE (room_id, start_time, end_time)
)
-- Indexes
CREATE INDEX idx_bookings_room_time ON bookings(room_id, start_time, end_time);
CREATE INDEX idx_bookings_user ON bookings(user_id);
CREATE INDEX idx_rooms_location ON rooms(location);
Read Replicas: 2-3 replicas for read-heavy operations (availability checks)
5. Critical Design Decisions
5.1 Preventing Double-Booking
Problem: Two users booking same room at same time
Solutions:
Option 1: Database-Level Locking
sql
BEGIN TRANSACTION;
SELECT * FROM bookings
WHERE room_id = ? AND start_time < ? AND end_time > ?
FOR UPDATE;
-- If no conflict, insert
INSERT INTO bookings ...;
COMMIT;
Option 2: Distributed Lock (Redis)
python
lock_key = f"room:{room_id}:{date}"
if redis.set(lock_key, "1", nx=True, ex=10):
# Check availability and book
# Release lock
redis.delete(lock_key)
Recommended: Combine both - Redis for speed, DB constraint as safety net
5.2 Handling High Read Load
Cache popular rooms and time slots
Read replicas for availability queries
Database partitioning by date/location
Denormalization for frequently accessed data
5.3 Consistency vs Availability Trade-off
Strong consistency needed for bookings (prevent double-booking)
Eventual consistency acceptable for:
Room search results
Analytics
Notification delivery
6. API Design Examples
Check Availability
text
GET /api/v1/rooms/available
Query params:
- location: string
- startTime: ISO8601
- endTime: ISO8601
- capacity: integer
- amenities: array
Response:
{
"rooms": [
{
"roomId": "uuid",
"name": "Conference Room A",
"capacity": 10,
"location": "Building 1, Floor 2",
"amenities": ["projector", "whiteboard"],
"hourlyRate": 50.00
}
],
"total": 25
}
Create Booking
text
POST /api/v1/bookings
Body:
{
"roomId": "uuid",
"startTime": "2025-11-25T09:00:00Z",
"endTime": "2025-11-25T11:00:00Z",
"purpose": "Team Meeting"
}
Response:
{
"bookingId": "uuid",
"status": "confirmed",
"confirmationCode": "ABC123",
"qrCode": "base64..."
}
7. Scalability Considerations
Horizontal Scaling
Stateless services: Easy to scale behind load balancer
Database sharding: Partition by location or date range
Cache sharding: Redis Cluster with consistent hashing
Vertical Scaling
Upgrade database instances for complex queries
More cache memory for higher hit rates
Auto-scaling
Scale service instances based on CPU/memory metrics
Pre-scale before known peak hours (Monday mornings)
8. Monitoring & Observability
Metrics:
Request latency (p50, p95, p99)
Error rates by service
Cache hit ratio
Database connection pool usage
Queue depth (notification service)
Logging:
Centralized logging (ELK stack, Splunk)
Distributed tracing (Jaeger, Zipkin)
Audit logs for all bookings
Alerts:
High error rates
Database replication lag
Cache failures
Service health checks failing
9. Security Considerations
Authentication: JWT tokens with short expiration
Authorization: Role-based access control (RBAC)
Data encryption: TLS in transit, AES-256 at rest
Rate limiting: Prevent DDoS and abuse
SQL injection prevention: Parameterized queries
PII protection: Hash/encrypt sensitive user data
10. Future Enhancements
ML-based recommendations: Suggest rooms based on history
Dynamic pricing: Surge pricing during peak hours
Mobile apps: iOS/Android native apps
Calendar integration: Sync with Google Calendar, Outlook
Analytics dashboard: Usage patterns, popular rooms
Recurring bookings: Support weekly meetings
Waiting list: Auto-book when cancellation occurs