SLIDE 24: Section Intro, High-Level Architecture
Section 4: High-Level Architecture

Every system design interview moves from requirements to architecture. This is where you translate the constraints from Sections 2 and 3 into components, connections, and communication patterns. The architecture you draw here becomes the map for every deep-dive section that follows.
This section covers the full ride-sharing architecture: 45 components across 8 clusters, built layer by layer from clients to data stores. The numbers from Section 3 (1.25M GPS updates/sec, 5M concurrent WebSocket connections, 250 ride requests/sec) directly determine which components exist and how they connect.
What We’ll Cover:
- How to draw a system architecture top-down in an interview
- Client applications: Rider, Driver, and Admin
- Entry layer: Load Balancer, API Gateway, WebSocket Gateway
- Auth Service and Rate Limiter
- Core services: four groups (User Management, Ride Engine, Business Logic, Real-Time)
- WebSocket architecture for real-time driver location streaming
- Synchronous vs asynchronous communication patterns
- Kafka event bus and message queue placement
- Service decomposition principles
- Complete architecture diagram with all connections
Interview Pacing:
This section maps to minutes 6-14 of a 45-minute interview. You have approximately 8 minutes to draw and explain the core architecture. Start from clients, build layer by layer, and explain as you draw. Do not draw all 45 boxes. Focus on the entry layer, the four service groups, and the primary data stores. Fill in details only when the interviewer asks you to go deeper.
Where This Fits:
The requirements from Section 2 tell us what to build. The capacity estimates from Section 3 tell us how big it needs to be. This section translates both into a concrete set of components and connections. Every section after this one (Data Flow in Section 5, Geospatial in Section 6, Matching in Section 7, Database Design in Section 8) is a deep dive into one area of this architecture.
SLIDE 25: How to Draw a System Architecture in an Interview
In an interview, your drawing order reveals your thinking process. Starting from the database and working up signals that you are thinking about storage before understanding the problem. Starting from the user and working down signals that you are designing for the user’s experience, then figuring out what supports it. Interviewers notice this.
The correct order is top-down: start with who uses the system, then how traffic reaches your backend, then what processes the requests, then where the data lives.

Note: in the actual system, Core Services sit above the Data Layer (services query databases, not the entry layer directly). The drawing order is top-down: clients first, then entry, then services, then data stores beneath them.
Why This Order Works:
Each layer constrains the design of the layer below it. You cannot design the API Gateway without knowing which clients connect to it. You cannot design the services without knowing what the gateway routes to them. You cannot choose databases without knowing what the services need to store. Drawing top-down lets each decision flow naturally from the one before it.
When you draw databases first, the interviewer sees you guessing at storage before understanding the access patterns. When you draw services before clients, you risk designing internal components that do not map to any user-facing requirement.
Common Mistake:
Drawing the entire diagram in silence, then narrating it afterward. This forces the interviewer to watch you draw for 3-4 minutes without understanding your reasoning. Instead, draw and explain simultaneously. Each box you add should come with one sentence explaining why it exists.
For example: “The driver app connects through a separate WebSocket Gateway because drivers maintain persistent connections streaming GPS data every 4 seconds. That is a fundamentally different traffic pattern from the rider’s HTTP requests, so they need separate infrastructure.”
Interview Tip:
You do not need to draw all 45 components in an interview. Draw the entry layer, the four service groups as labeled clusters, and the primary data stores. That gives the interviewer a complete picture in 6-8 minutes. They will ask you to go deeper into specific areas, and that is when you expand a cluster into its individual services.
SLIDE 26: Client Applications: Rider, Driver, Admin
The system has three client types, and they have fundamentally different interaction patterns with the backend. Understanding these differences drives the entire entry layer design.

Rider App (Mobile)
The rider opens the app, sees nearby drivers on a map, requests a ride, tracks the driver in real time, and pays. The interaction is bursty: short periods of high engagement during a ride (5-20 minutes) separated by hours or days of inactivity. The app communicates primarily via HTTP request-response, with a temporary WebSocket connection opened during an active ride for real-time tracking.
Driver App (Mobile)
Once a driver goes online, the app opens a persistent WebSocket connection and begins streaming GPS coordinates to the backend every 3-4 seconds. This connection stays open for the driver’s entire shift, typically 8-12 hours. The driver receives ride offers, navigation updates, and trip events through this same connection. This is a fundamentally different network pattern from the rider app: continuous, long-lived, and write-heavy.
Admin Dashboard (Web)
Operations teams use a browser-based dashboard to handle support tickets, monitor system health, manage city configurations, and investigate disputes. Traffic is low volume, human-speed, and entirely HTTP-based.
The Architectural Insight:
The driver app’s persistent WebSocket connection is what separates this system from a typical content platform. Instagram, YouTube, and Twitter are almost entirely HTTP request-response. A ride-sharing platform must support 5 million persistent connections streaming data continuously. This single requirement creates the need for a dedicated WebSocket Gateway, which is the next slide.
SLIDE 27: Entry Layer: Load Balancer and API Gateway
All client traffic enters the system through the entry layer. Two components handle this: the Load Balancer and the API Gateway. They operate at different network layers and solve different problems.
Load Balancer (Layer 4)
The Load Balancer operates at the transport layer (TCP/UDP). It sees incoming connections as raw TCP streams and distributes them across backend servers using algorithms like round-robin or least-connections. It does not inspect the content of requests. Its job is to prevent any single server from being overwhelmed and route traffic to healthy servers.
For this system, the Load Balancer splits traffic between two destinations: the API Gateway (for HTTP traffic from rider apps and admin dashboard) and the WebSocket Gateway (for persistent connections from driver apps). This split happens based on the destination port or protocol indicator.
API Gateway (Layer 7)
The API Gateway operates at the application layer. It inspects the full HTTP request (URL path, headers, body) and makes routing decisions based on that content. It handles authentication validation, rate limiting, request transformation, and routing to the correct backend service.

Why Both, Not Just One:
Combining both roles into a single component creates a bottleneck. Every raw TCP connection would need full HTTP parsing, including health check pings and keep-alive probes that do not need application-level inspection. Separating L4 from L7 lets the Load Balancer handle raw connection distribution at near-zero latency, while the API Gateway focuses on application logic for the requests that need it.
The separation is also critical because the Load Balancer needs to route two fundamentally different traffic types (HTTP and WebSocket) to two different gateway services. A single gateway trying to handle both short-lived HTTP requests and long-lived WebSocket connections would need to optimize for contradictory resource profiles.
SLIDE 27B: Entry Layer: WebSocket Gateway
The WebSocket Gateway is the component that makes this architecture fundamentally different from most web applications. It exists because 5 million active drivers maintain persistent, bidirectional connections for the duration of their shifts.
Why a Separate Gateway:
A standard API Gateway handles short-lived HTTP requests. A request arrives, gets processed in 50-500ms, and the connection closes. The gateway optimizes for high request throughput.
A WebSocket connection is the opposite. It stays open for hours, with small messages flowing in both directions every few seconds. The gateway optimizes for high connection count: hold as many open connections as possible with minimal per-connection overhead.
These two optimization targets conflict. An API Gateway tuned for throughput allocates significant memory per request for parsing and transformation, then frees it. A WebSocket Gateway allocates minimal memory per connection but must hold it for hours. Running both workloads on the same infrastructure means neither performs well.

What Flows Through the WebSocket Gateway:
Upstream (driver to server): GPS coordinates every 3-4 seconds, ride acceptance/rejection responses, trip status updates (started, completed).
Downstream (server to driver): dispatch offers with ride details, navigation updates, trip cancellation notices, surge zone changes.
Scale and Resource Profile:
5 million concurrent WebSocket connections. Each connection sends approximately 100 bytes upstream every 4 seconds. That is 1.25 million messages per second of sustained inbound throughput, roughly 125 MB/sec of raw GPS data. A single server can typically handle 50,000-100,000 concurrent WebSocket connections depending on hardware and message frequency, so the WebSocket Gateway layer needs 50-100 servers for connection capacity alone.
Connection State and Routing:
Each WebSocket connection is pinned to a specific gateway server for its lifetime. If the Dispatch Service needs to send a ride offer to driver_A, it must know which gateway server holds that driver’s connection. This mapping (driver_id to gateway_server_id) is stored in Redis and updated whenever connections open, close, or migrate due to server rebalancing. This statefulness is one of the key scaling challenges addressed in Section 11.
Interview Tip:
When an interviewer asks “how does the driver receive the ride offer?”, this is the answer. The driver does not poll for offers. The server pushes the offer through the existing WebSocket connection in real time. Mentioning WebSocket push and explaining why HTTP polling fails at 5 million drivers (each polling every second would generate 5M requests/sec of pure overhead) demonstrates that you understand the real-time constraints.
SLIDE 28: Entry Layer: Auth Service and Rate Limiter
Two more components sit in the entry layer, both designed to reject invalid or excessive traffic before it reaches core services: the Auth Service and the Rate Limiter.
Auth Service
Every request that passes through the API Gateway includes a JWT (JSON Web Token) that identifies the user and their role (rider, driver, or admin). The Auth Service validates this token on every request: checks the signature, verifies expiration, and extracts the user_id and role for downstream services.
For driver WebSocket connections, authentication happens once during the connection upgrade handshake. After that, the connection is considered authenticated for its lifetime. There is no per-message re-authentication, which is another reason WebSocket is more efficient than HTTP polling for long sessions.
Token refresh is important for drivers specifically. A driver shift can last 8-12 hours, and JWT tokens typically expire in 15-60 minutes. The driver app must refresh its token periodically without dropping the WebSocket connection. This is handled by sending a refresh request over the existing WebSocket connection, and the gateway swaps the token internally.
Rate Limiter
The Rate Limiter protects the system from abuse and overload at three levels:
Rate limit state (request counts per user per window) is stored in Redis using sliding window counters. Redis is fast enough for per-request lookups and shared across all API Gateway instances so that a user cannot bypass limits by hitting different servers.
Why Both at the Edge:
Auth and rate limiting execute before any request reaches a core service. An unauthenticated request is rejected at the gateway, never consuming Trip Service or Matching Service resources. A rate-limited request returns 429 immediately without queuing. This is critical during demand spikes: if 40,000 concert-goers all request rides simultaneously, the rate limiter protects the Matching Service from being overwhelmed while the system processes requests in manageable batches.
SLIDE 29: Core Services: Four Groups Overview
Behind the entry layer, the system’s business logic is organized into four service groups. Each group handles a distinct domain of the ride-sharing problem, and the groups communicate through well-defined APIs and events.

The Four Groups:
User Management handles everything about who uses the system. Rider registration, driver onboarding, profile management, vehicle records. This data changes rarely (a driver updates their vehicle once, not every trip) and is read frequently (every ride request needs rider and driver information).
Ride Engine handles the core trip lifecycle. Receiving a ride request, finding and scoring candidate drivers, dispatching the offer, managing the trip from start to finish. This is the central coordination layer that orchestrates the other groups.
Business Logic handles money and pricing. Fare calculation, surge pricing, payment processing, promotions and credits. These services are separated from the Ride Engine because pricing rules and payment integrations change independently from the trip orchestration logic.
Real-Time Services handle continuous data streams. GPS location ingestion, real-time trip tracking on the map, push notifications. These services are defined by their interaction pattern: persistent connections, high throughput, low latency.
Why Four Groups, Not One “Ride Service”:
A monolithic “Ride Service” that handles everything from user lookup to fare calculation to payment processing would be a single point of failure, impossible to scale independently, and a coordination nightmare for engineering teams. The four-group decomposition lets each group scale according to its own bottleneck: User Management scales on read throughput, Ride Engine on trip concurrency, Business Logic on compute, and Real-Time on connection count and write throughput.
SLIDE 30: User Management: Rider, Driver, and Profile Services
The User Management group contains three services that manage identity and profile data for everyone who uses the system.
Rider Service
Handles rider registration, authentication, ride history, saved addresses, and payment method management. When a rider requests a ride, the Ride Engine calls the Rider Service to fetch the rider’s profile and payment method. This is a read-heavy service: rider profiles are written once (registration, occasional updates) and read on every ride.
Driver Service
Handles driver registration, document verification, vehicle association, online/offline status, and the driver’s current acceptance rate and rating. The Driver Service manages a critical piece of state: whether a driver is currently online and available for matching. When a driver goes online, the Driver Service updates the driver’s status and triggers the Location Service to begin tracking their position.
Profile Service
Shared profile infrastructure used by both Rider and Driver Services. Manages common elements: name, email, phone, profile photo, notification preferences. In some architectures this is not a separate service but a shared module within Rider and Driver Services. The decision depends on how much shared logic exists.
Why Separate Rider and Driver Services:
Riders and drivers have fundamentally different data models and lifecycles. A rider has saved addresses and payment methods. A driver has vehicles, licenses, insurance documents, and compliance records. A rider’s lifecycle is: register, request rides occasionally. A driver’s lifecycle is: apply, submit documents, get approved, go online daily, receive ratings, track earnings. Combining these into one “User Service” would create a bloated service with two unrelated domains sharing a codebase and database.
Data Characteristics:
Total user base: approximately 150 million riders, 5 million drivers. These are small tables by database standards. The rider table fits comfortably in a single PostgreSQL instance with read replicas. The driver table is even smaller. There is no need for sharding at this scale, though read replicas are useful because every ride request reads from these tables.
SLIDE 31: Ride Engine: Trip Service (Orchestrator)
The Trip Service is the central coordinator of the ride lifecycle. It does not perform matching, pricing, or payment itself. Instead, it orchestrates calls to the services that do, managing the overall trip state as each step completes.

What the Trip Service Owns:
The trip state machine. Every trip passes through a defined set of states, and the Trip Service is the single authority on which state a trip is in at any moment.
When a rider requests a ride, the Trip Service creates a trip record in the “requested” state. It calls the Matching Service to find a driver. When a match is found, it transitions to “matched.” It calls the Dispatch Service to send the offer. When the driver accepts, it transitions to “driver_en_route.” Each transition is recorded as an immutable event in the trip_state_history table for auditability and debugging.
Why an Orchestrator Pattern:
Without a central coordinator, each service would need to know about every other service’s state. The Matching Service would need to know about pricing, the Dispatch Service would need to know about payment, and failure handling would be scattered across the codebase. The Trip Service acts as a single point of control: it knows the current state, it decides which service to call next, and it handles failures by implementing retry logic and compensation actions (like releasing a locked driver if payment fails).
The Orchestrator Trade-Off:
The Trip Service is a coordination bottleneck. If it goes down, no new rides can be processed. This is an intentional trade-off: centralized control makes the system easier to reason about, debug, and audit, at the cost of a single dependency. The mitigation is horizontal scaling (multiple Trip Service instances behind a load balancer) and ensuring each trip is handled by exactly one instance (using trip_id-based routing or optimistic locking).
SLIDE 31B: Ride Engine: Matching and Dispatch Services
Matching Service
The Matching Service answers one question: given a ride request with a pickup location and vehicle type, which available driver is the best assignment?
This is not a simple “find the closest driver” lookup. The Matching Service runs a candidate pipeline: query the spatial index for nearby drivers (Section 6 covers this in depth), filter by eligibility (vehicle type, status, capacity), score candidates on multiple factors (ETA, rating, acceptance history, heading direction), and return a ranked list.
At low demand, the Matching Service uses greedy assignment: pick the top-ranked candidate for each request independently. During high demand (surge periods, event spikes), it switches to batch matching: collect requests over a short window (2-5 seconds) and solve the assignment as an optimization problem across all pending requests. Section 7 covers the matching algorithm in full detail.
Dispatch Service
The Dispatch Service handles the communication between the system and the matched driver. It takes the top candidate from the Matching Service and sends a ride offer through the WebSocket Gateway to that driver’s app. The offer includes pickup location, estimated trip distance, estimated fare, and pickup ETA.
The driver has 15 seconds to accept or decline. If the driver declines or the timer expires, the Dispatch Service sends the offer to the next candidate in the ranked list. After 3-5 consecutive declines, it triggers a re-query: the Matching Service runs the candidate pipeline again with an expanded search radius, because the original candidates may no longer be the best options.
Why Matching and Dispatch Are Separate Services:
Matching is computation: run the candidate pipeline, score, rank. It is CPU-bound and benefits from horizontal scaling on compute resources. Dispatch is communication: send a message, wait for a response, handle timeouts. It is I/O-bound and benefits from async event-driven architecture. Combining them would force a single service to optimize for both compute throughput and concurrent I/O, which are different scaling profiles.
SLIDE 31C: Ride Engine: ETA Service
The ETA Service calculates how long it will take to get from point A to point B on the road network, accounting for real-time traffic conditions.
Two Types of ETA:
Pre-trip ETA (pickup time): how long until the matched driver reaches the rider’s pickup location. This is shown to the rider immediately after matching and continuously updated as the driver moves.
In-trip ETA (destination arrival): how long until the rider arrives at their destination. This is shown to both rider and driver during the trip and recalculated every 30-60 seconds based on current position and traffic.
How It Works:
The road network is modeled as a weighted directed graph. Intersections are nodes, road segments are edges, and edge weights represent travel time based on distance, speed limit, and real-time traffic data. The ETA Service queries a Maps API (Google Maps Platform, Mapbox, or OSRM) that maintains this graph and runs shortest-path algorithms to return the fastest route and estimated duration.
Cost Consideration:
Maps API calls are priced per request. At 20 million trips per day with multiple ETA calls per trip (initial estimate, driver en route updates, in-trip updates), the Maps API bill becomes one of the largest infrastructure costs. Uber reportedly spent over $58 million per year on Google Maps before building their own mapping platform. Strategies to manage cost: cache ETAs for common routes during similar traffic windows, batch nearby ETA requests, reduce update frequency from every 30 seconds to every 60 seconds during stable segments of a trip.
Fallback Strategy:
If the Maps API is unavailable or slow, the ETA Service falls back to straight-line distance estimates with a correction factor (typically 1.3-1.5x for urban areas), or cached historical ETAs for the same route during the same time of day. The fallback is less accurate but keeps the system operational.
Interview Tip:
When discussing ETA, interviewers often test whether you recognize the cost and latency implications of external API dependencies. Mentioning Maps API pricing and fallback strategies demonstrates operational awareness, not just design ability.
SLIDE 32: Business Logic: Pricing and Surge Engine
The Business Logic group handles the financial side of every ride. Two services handle the core pricing problem.
Pricing Service
Calculates the fare for a trip. The base formula:
fare = base_fare + (distance_km x distance_rate) + (duration_min x time_rate)
fare = fare x surge_multiplier
fare = fare - promotions
fare = max(fare, minimum_fare)
The Pricing Service is called twice per trip. Once before the trip starts (to show the rider an estimated fare) and once after the trip completes (to calculate the actual fare based on real distance driven and actual duration). If the actual fare exceeds the estimate by more than a threshold (e.g., 20%), the system caps at the estimate to protect the rider.
Base rates, distance rates, and time rates vary by city, vehicle type, and time of day. These configurations are stored in a pricing rules table and cached in Redis for fast lookup.
Surge Engine
The Surge Engine calculates the surge multiplier per geographic zone in real time. It divides the city into pricing zones (aligned with geofence boundaries or hexagonal H3 cells), counts available drivers (supply) and recent ride requests (demand) per zone, and computes a ratio.
If supply/demand ratio drops below a threshold (e.g., 0.5), the surge multiplier increases. If the ratio is above another threshold (e.g., 2.0), the multiplier stays at 1.0 (no surge). Between thresholds, the multiplier increases gradually. Typical range: 1.0x to 3.0x, with a hard cap to prevent extreme pricing.
Section 10 covers the full surge pricing pipeline, including oscillation prevention, smoothing windows, and the real-time computation architecture.
Why Pricing and Surge Are Separate Services:
The Pricing Service applies a deterministic formula using rates and the surge multiplier as inputs. The Surge Engine is a real-time analytics pipeline that ingests GPS updates and ride request events to compute supply-demand ratios. These are fundamentally different workloads: one is a lookup plus arithmetic, the other is a streaming computation. They change at different rates (pricing rules change monthly, surge logic is continuously tuned) and are maintained by different teams.
SLIDE 32B: Business Logic: Payment and Promotions Services
Payment Service
The Payment Service manages the financial transaction for each trip using a two-phase approach:
Phase 1 (Authorization): When a ride is matched, the Payment Service sends an authorization request to the rider’s payment provider (Stripe, Braintree, or direct card network) for the estimated fare. This puts a hold on the funds without actually charging the rider. If authorization fails (insufficient funds, expired card), the system can cancel the match before the driver starts driving.
Phase 2 (Capture): When the trip completes, the Pricing Service calculates the actual fare. The Payment Service then captures the authorized amount (or adjusts if the actual fare differs from the estimate). The rider’s card is charged, and the transaction is recorded.
Driver settlement is asynchronous. Drivers are not paid per-trip in real time. Instead, the Payment Service accumulates completed trip earnings and processes payouts on a schedule (daily or weekly), minus the platform’s commission. This batch settlement reduces the number of outbound payment transactions from 20 million per day (one per trip) to roughly 5 million per week (one per driver per payout cycle).
Promotions Service
Manages coupons, credits, referral bonuses, and first-ride discounts. The Promotions Service is called after the Pricing Service calculates the surge-adjusted fare, applying any valid promotions to reduce the final amount charged to the rider.
Business rules for promotions: some discounts are percentage-based, some are fixed credits. Some stack with surge pricing, some do not. Some have usage limits (one per rider) and expiration dates. The Promotions Service validates all of these rules before applying a discount.
Why Payment Is Decoupled from Trip Completion:
If the Payment Service goes down, trips should still complete. The rider gets out of the car, the driver marks the trip as finished, and the payment is retried asynchronously. This decoupling prevents payment infrastructure outages from blocking the core ride experience. The same principle applies to refunds and disputes, which are processed as separate async workflows.
SLIDE 33: Real-Time Services: Location, Notification, Tracking
The Real-Time group handles the continuous data streams that define a ride-sharing platform’s live experience.
Location Service
The highest-throughput service in the entire system. It ingests 1.25 million GPS updates per second from active drivers, writes each update to a spatial index (Redis with GEOADD), and serves “find nearby drivers” queries for ride matching. It also writes location history asynchronously to a time-series database for analytics and trip reconstruction.
The Location Service has two distinct paths. The write path handles incoming GPS coordinates from drivers via the WebSocket Gateway. The read path handles nearby-driver queries from the Matching Service. These paths are separated internally to prevent write volume from blocking read latency. Section 6 covers the Location Service architecture in full detail.
Notification Service
Sends one-time alerts to riders and drivers when specific events occur: ride matched, driver arriving, trip completed, payment processed, receipt available. These are push notifications delivered through Apple Push Notification Service (APNs) or Firebase Cloud Messaging (FCM) to backgrounded mobile apps.
The Notification Service subscribes to Kafka events (ride_matched, trip_completed, payment_captured) and maps each event type to a notification template. It is a consumer, not a producer: it reacts to events generated by other services.
Tracking Service
Powers the real-time map visualization in both rider and driver apps during an active trip. When a rider watches their driver approach on the map, the Tracking Service is delivering a stream of interpolated positions via WebSocket.
The Tracking Service consumes the driver’s raw GPS positions from the Location Service and applies smoothing (to remove GPS jitter) and interpolation (to provide fluid animation between 4-second GPS updates). It then pushes the smoothed positions to the rider’s app through a dedicated WebSocket channel opened when the trip begins.
What All Three Share:
All three services depend on the WebSocket infrastructure covered in SLIDE 34. Location and Tracking need the persistent driver connection for position data. Notification uses push for backgrounded apps but WebSocket for in-app alerts during an active ride.
SLIDE 34: WebSocket Architecture: How Driver Location Streaming Works
This slide shows the end-to-end flow of how a driver’s GPS coordinates move from the driver’s phone to the spatial index where they can be queried by the Matching Service.
Write Path (Driver Position Update):

Step 1: The driver app reads the phone’s GPS sensor and sends a small message (approximately 100 bytes: driver_id, lat, lng, timestamp, heading, speed) through the existing WebSocket connection.
Step 2: The WebSocket Gateway routes the message to the Location Service. The Location Service validates the coordinates (rejecting impossible values like latitude > 90).
Step 3: The Location Service executes a Redis GEOADD command to update the driver’s position in the spatial index. This is the synchronous, latency-critical path.
Step 4: Asynchronously (not blocking the write path), the Location Service publishes the position to Kafka for downstream consumers: the Location History writer (time-series DB), the Tracking Service (for active trip visualization), and the Surge Engine (for supply counting).
Read Path (Nearby Driver Query):

When the Matching Service needs to find drivers near a pickup location, it calls the Location Service with the rider’s coordinates and a search radius (typically 5km). The Location Service executes a Redis GEOSEARCH command, which returns all drivers within that radius sorted by distance. The result includes each driver’s ID, current position, and distance from the pickup point.
Key Insight:
The write path runs at 1.25 million messages per second, continuously. The read path runs at approximately 250 queries per second (one per ride request), in bursts. These are completely different volume and latency profiles, which is why the Location Service separates them internally. Write handlers can be scaled for throughput while read handlers are scaled for low latency.
SLIDE 34B: WebSocket vs HTTP Polling: Why Persistent Connections
Three approaches exist for getting real-time data from a server to a client: short polling, long polling, and WebSocket. For a system with 5 million drivers sending GPS coordinates every 4 seconds, the choice has massive infrastructure implications.
The Math:
With short polling at 4-second intervals: 5M drivers x 1 request per 4 seconds = 1.25M HTTP requests/sec. Each request carries ~500 bytes of HTTP headers for a 100 byte GPS payload. That is 625 MB/sec of header overhead alone, compared to ~125 MB/sec total with WebSocket (where frame overhead is ~6 bytes per message).
With WebSocket: 5M persistent connections consume memory on the gateway servers (~50-100 servers), but each GPS update is a tiny frame with minimal overhead. Total bandwidth: ~131 MB/sec (payload + minimal framing).
The Trade-Off:
WebSocket is more complex to operate. Connections are stateful and pinned to specific servers. If a server goes down, all connections on that server must reconnect and re-authenticate. Load balancing is more complex because you cannot simply round-robin requests. But for a system that needs continuous bidirectional communication at this scale, WebSocket is the only viable approach. The infrastructure complexity is justified by the 5x bandwidth reduction and real-time latency.
SLIDE 35: Synchronous vs Asynchronous Communication Patterns
Services in this architecture communicate in two ways: synchronous (the caller waits for a response) and asynchronous (the caller fires a message and moves on). The choice between them is not arbitrary. It follows one principle: if the user is waiting for the result, the call is synchronous. If the user’s action is already complete, the downstream processing is asynchronous.
The Pattern:
Everything in the critical ride flow (request, match, dispatch, accept) is synchronous because the rider and driver are actively waiting. Everything after the trip ends (payment capture, notifications, analytics, settlement) is asynchronous because the user’s experience is already complete.
Interview Tip:
When walking through a data flow in an interview, labeling each call as sync or async and explaining why is a strong signal. It shows that you understand the difference between user-facing latency (which must be fast) and background processing (which can tolerate delays and retries).
SLIDE 35B: Kafka Event Bus and Message Queue Placement
Kafka sits between the core services and all downstream consumers. When a trip state changes, the Trip Service publishes an event to Kafka. Multiple consumers independently read and process that event without the Trip Service knowing or caring about them.

Why Kafka, Not Direct Service-to-Service Calls:
Without Kafka, the Trip Service would need to call Payment, Analytics, and Notification directly after every state change. If Analytics is slow, it slows down the Trip Service. If Notification is down, the Trip Service needs to handle the failure. Adding a new consumer (say, a Fraud Detection service) requires modifying the Trip Service code.
With Kafka, the Trip Service publishes one event. Each consumer reads at its own pace. If Analytics is slow, it falls behind on its Kafka consumer group but the Trip Service is unaffected. If Notification is down, events accumulate in the topic and are processed when the service recovers. Adding a new consumer requires zero changes to the Trip Service.
Key Events Published to Kafka:
Event-Carried State Transfer:
Each Kafka event carries enough data for consumers to process it without making additional API calls. For example, the ride_completed event includes trip_id, rider_id, driver_id, distance, duration, and the calculated fare. The Payment Service can capture the payment without calling the Pricing Service again. This reduces inter-service coupling and eliminates the risk of inconsistency between what the Pricing Service calculated and what the Payment Service charges.
Dead Letter Queue:
Any event that a consumer fails to process (malformed payload, downstream timeout, schema mismatch) is routed to a Dead Letter Queue instead of being silently dropped. The operations team investigates DLQ entries, fixes the root cause, and replays the events. This is covered in more detail in Section 13.
SLIDE 36: Service Decomposition: Why These Boundaries
The system has 15+ individual services organized into four groups. The boundaries between services are not arbitrary. They follow three principles.
Principle 1: Each Service Owns Its Data
The Trip Service owns the trips table. The Payment Service owns the payments table. The Location Service owns the spatial index. No service reads or writes another service’s database directly. If the Payment Service needs trip details, it calls the Trip Service API or reads from a Kafka event. This prevents tight coupling between service internals and makes it possible to change a service’s database schema or technology without affecting any other service.
Principle 2: Services Align with Team Boundaries
Each service or service group maps to an engineering team. The Pricing team can deploy a new fare formula without coordinating with the Matching team. The Payment team can switch payment providers without involving the Trip team. This organizational alignment (often called Conway’s Law) reduces coordination overhead and deployment risk.
Principle 3: Services Scale Independently
The Location Service handles 1.25M writes/sec and runs on Redis. The Rider Service handles a few thousand reads/sec and runs on PostgreSQL with read replicas. These need completely different infrastructure. Combining them into one service would force shared scaling decisions: scaling Redis for location throughput would waste money for the rider data that fits in a small PostgreSQL instance.
The Anti-Pattern: Monolithic “Ride Service”
A common interview mistake is designing a single “Ride Service” that handles matching, pricing, dispatch, and trip state. This creates a service that cannot scale any dimension independently, cannot be deployed without risking all dimensions, requires every engineer on the team to understand every domain, and turns every incident into a company-wide outage.
When Decomposition Goes Too Far:
More services is not always better. Each service boundary adds network latency (inter-service calls), operational overhead (deployment pipelines, monitoring, on-call), and debugging complexity (distributed tracing across services). If two pieces of logic always change together and are always called together, they probably belong in the same service. The Rider Service and Driver Service are separate because their data models differ. The Matching Service and Dispatch Service are separate because their scaling profiles differ. But Profile Service functionality could reasonably live inside Rider/Driver Services if the shared logic is minimal.
SLIDE 37: Complete Architecture: All Components Connected
This is the reference diagram for the entire system. Every component discussed in this section is shown with its connections.

Full Component List by Cluster:
Entry Layer (5 components): Load Balancer, API Gateway, WebSocket Gateway, Auth Service, Rate Limiter
User Management (3 components): Rider Service, Driver Service, Profile Service
Ride Engine (4 components): Trip Service (orchestrator), Matching Service, Dispatch Service, ETA Service
Business Logic (4 components): Pricing Service, Surge Engine, Payment Service, Promotions Service
Real-Time Services (3 components): Location Service, Notification Service, Tracking Service
Data Layer (5+ components): PostgreSQL (users, trips, payments), Redis (positions, sessions, cache), TimescaleDB or Cassandra (location history), Kafka (event bus), Elasticsearch (search, logs)
External Services (2+ components): Maps API (Google Maps, Mapbox), Payment Providers (Stripe, Braintree)
Observability (4 components): Logging, Metrics, Distributed Tracing, Alerting
Key Data Flows to Trace:
The three primary data flows (covered in depth in Section 5) that cross these clusters:
- Ride request flow: Rider App → API Gateway → Trip Service → Matching Service → Location Service → Dispatch Service → WebSocket Gateway → Driver App
- Location update flow: Driver App → WebSocket Gateway → Location Service → Redis (spatial index) + Kafka → TimescaleDB (history)
- Trip completion flow: Driver App → Trip Service → Pricing Service → Kafka → Payment Service → Notification Service
This slide is the screenshot reference. Every section that follows will highlight one area of this diagram and go deep.
SLIDE 38: How to Narrate an Architecture Diagram
Drawing the architecture is half the task. The other half is narrating it clearly enough that the interviewer follows your reasoning without asking you to repeat yourself.
The Three Rules of Architecture Narration:
Rule 1: Draw and explain simultaneously. Every box you add to the whiteboard gets one sentence explaining why it exists. Do not draw everything first and explain later. The interviewer should never look at a box without knowing what it does.
Rule 2: Follow the request path, not the component list. Do not narrate by listing components (“here is the API Gateway, here is the Auth Service, here is the Trip Service”). Instead, narrate by following a user action through the system: “The rider opens the app and taps Request Ride. That request hits the Load Balancer, which routes it to the API Gateway. The Gateway validates the JWT token, checks the rate limit, and routes to the Trip Service…”
Rule 3: Label each connection with its communication pattern. When you draw an arrow between two services, say whether it is synchronous or asynchronous and why. “The Trip Service calls the Matching Service synchronously because the rider is waiting. After the trip completes, the Trip Service publishes an event to Kafka asynchronously because payment processing does not need to block.”
Common Narration Mistakes:
Starting with the database: “First, we have PostgreSQL for storing user data…” This signals you are thinking about storage before understanding the problem.
Naming every component without explaining relationships: “We have a Matching Service, a Dispatch Service, a Pricing Service…” This is a list, not an architecture. The value is in how they connect, not that they exist.
Skipping failure handling entirely: interviewers expect you to mention at least one failure scenario while narrating. “If the Matching Service does not find a driver within 10 seconds, the Trip Service expands the search radius and retries.”
SLIDE 39: Worked Example: Drawing the Core Architecture in 8 Minutes
This is a timed walkthrough showing exactly how to draw and narrate the architecture in an interview. 8 minutes, broken into four phases.
Minutes 1-2: Clients and Entry Layer
Draw the three client boxes (Rider App, Driver App, Admin). Below them, draw the Load Balancer. Explain: “Traffic enters through an L4 Load Balancer. It splits traffic between two gateways.” Draw the API Gateway and WebSocket Gateway side by side. Explain: “HTTP request-response traffic from riders goes to the API Gateway. Persistent WebSocket connections from drivers go to a separate WebSocket Gateway because drivers stream GPS every 4 seconds, and that is a fundamentally different traffic pattern.”
Add Auth Service and Rate Limiter next to the gateways. One sentence: “Auth validates JWT tokens and rate limiting protects backend services during demand spikes.”
Minutes 3-5: Core Service Groups
Draw four labeled clusters below the entry layer. Do not draw individual services yet. Label them: User Management, Ride Engine, Business Logic, Real-Time.
Explain each with one sentence: “User Management handles rider and driver profiles. Ride Engine orchestrates the trip lifecycle, including matching and dispatch. Business Logic handles pricing, surge, and payments. Real-Time handles GPS location ingestion and push notifications.”
Then expand Ride Engine (the most important cluster): Trip Service in the center, with Matching and Dispatch connected. Explain the orchestrator pattern: “The Trip Service coordinates the entire ride. It calls Matching to find a driver, Dispatch to send the offer, and Pricing to calculate the fare.”
Minutes 6-7: Data Layer and External Services
Draw the data stores below the service clusters: PostgreSQL (users, trips, payments), Redis (positions, sessions), Kafka (event bus). Add the Maps API as an external service connected to the ETA Service.
Explain the key data separation: “Relational data like users and trips lives in PostgreSQL. Real-time driver positions live in Redis because we need 1.25 million writes per second. Kafka handles async event processing for payments, analytics, and notifications after trip completion.”
Minute 8: Key Connections and One Failure Case
Draw the primary arrows connecting the clusters. Trace the ride request flow in one pass. Then mention one failure scenario: “If the Matching Service cannot find a driver, the Trip Service expands the search radius. If no driver is found after 3 retries, the rider sees ‘no drivers available’ and can retry manually.”
SLIDE 40: Wrong vs Right: Architecture Presentation
Mistake 1: Drawing everything, then explaining
Wrong: Spending 4 minutes silently drawing 20 boxes and 15 arrows, then spending 4 minutes walking through what you drew.
Better: Draw each component as you explain it. The interviewer should never look at a box they do not understand.
Why it matters: silent drawing wastes interview time and creates anxiety for the interviewer who cannot assess your thinking until you start talking.
Mistake 2: Starting from the database
Wrong: “Let me start with the data layer. We will use PostgreSQL for users and trips, Redis for caching, and Kafka for events.”
Better: “The rider opens the app on their phone. The request hits a Load Balancer, then an API Gateway that validates the token and routes to the Trip Service.”
Why it matters: starting from the user shows you are thinking about the problem, not just listing technologies.
Mistake 3: Flat list of services without relationships
Wrong: “We have a Trip Service, a Matching Service, a Dispatch Service, a Pricing Service, a Payment Service, and a Location Service.”
Better: “The Trip Service orchestrates the ride. It calls the Matching Service to find a driver, the Dispatch Service to send the offer, and the Pricing Service to calculate the fare. These are synchronous calls because the rider is waiting.”
Why it matters: the value of an architecture is in the relationships and communication patterns, not the component inventory.
Mistake 4: No failure handling mentioned
Wrong: presenting the architecture as though nothing ever fails.
Better: mentioning at least one failure scenario per major flow. “If the driver does not accept within 15 seconds, we cascade to the next candidate. After 3 declines, we re-query the spatial index with a larger radius.”
Why it matters: at the senior level, interviewers expect you to proactively discuss what happens when things go wrong. You should not wait for them to ask.
Mistake 5: Trying to draw all 45 components
Wrong: spending the entire interview drawing a detailed diagram of every service and database.
Better: drawing 4 clusters with labels (User Management, Ride Engine, Business Logic, Real-Time), then expanding one cluster when the interviewer asks for depth.
Why it matters: a 45-minute interview does not have time for 45 components. Show the full picture at cluster level, then go deep where asked.
SLIDE 41: Interview Practice: High-Level Architecture
Practice Exercise:
Set a timer for 8 minutes. Using the structure from SLIDE 39, draw and narrate the ride-sharing architecture on a whiteboard or blank paper. Record yourself if possible.
Checklist: What Your Architecture Should Include
Entry layer: Load Balancer, API Gateway, WebSocket Gateway (with one sentence on why WebSocket is separate).
Four service groups: named and labeled, with at least the Trip Service expanded to show the orchestrator pattern.
Data stores: PostgreSQL, Redis, and Kafka, each with one sentence on what they store and why that technology was chosen.
Communication patterns: at least two sync calls and two async events labeled and explained.
One failure scenario: mentioned proactively, not waiting for the interviewer to ask.
Self-Assessment:
Did you start from clients and work down, or from databases and work up? Did you explain each component as you drew it, or draw first and explain later? Did you label sync vs async on at least two connections? Did you mention a failure case without being prompted? Did you finish in under 8 minutes?
How This Connects Forward:
Section 5 (Data Flow) traces three complete request paths through this architecture with sequence diagrams. Section 6 (Geospatial) expands the Location Service box into its full spatial indexing design. Section 7 (Matching and Dispatch) expands the Ride Engine cluster into its matching algorithm and dispatch flow. Section 8 (Database Design) expands the Data Layer into full schemas, indexes, and sharding strategy.
SLIDE 42: Section Cheat Sheet
Key Components:
Common Interview Mistakes:
Key Numbers to Remember:
5M concurrent WebSocket connections (drivers)
1.25M GPS updates/sec (5M drivers / 4-sec interval)
250 ride requests/sec average, 500-700/sec peak
50-100 WebSocket Gateway servers (50K-100K connections per server)
125 MB/sec inbound GPS data
15 seconds: dispatch offer acceptance window
8 minutes: target time to draw full architecture in interview
4 service groups: User Mgmt, Ride Engine, Business Logic, Real-Time
How This Connects to Other Sections: