Introduction: moving from the edge to the core
So far, the series has:
- Turned a rainy-night story into concrete requirements and a trip state machine.
- Drawn a high-level architecture and explained the API gateway’s role as the front door for all traffic.
This article goes one layer deeper into the core domain services: Trip, Dispatch, Location, Pricing, Payment, plus User/Driver and Notification. You will see what each service does, how they talk to each other, and some simple APIs, flows, and data models for an Uber‑like backend.
Core domain services at a glance
These services are the “brain and memory” of the ride sharing platform. They sit behind the API gateway and implement all business logic.
core services map

- Trip Service (TR) sits in the center and owns the trip lifecycle, state, and core records. It exposes trip-related APIs and emits trip events to the message bus.
- Dispatch Service consumes trip events (like trip.requested) and matches riders with drivers based on data from Location and Driver Services.
- Location Service tracks where drivers are and supports “nearby drivers” queries and live streaming.
- Pricing Service estimates and finalizes fares using distance/time plus demand-supply info.
- Payment Service charges riders and manages payouts for drivers.
- User and Driver Services store profiles, preferences, and ratings; they are consulted by other services whenever rider or driver context is needed.
- Notification Service sends push and in-app messages so rider and driver stay in sync as the trip state evolves.
The message bus (MQ) connects them with events like trip.requested, trip.driver_assigned, trip.completed, and trip.paid in a loosely coupled way.
Trip Service: the source of truth for rides
Trip Service is the central service in any Uber system design. It owns the trip entity and enforces the trip state machine defined in Article 1.
Responsibilities
- Create new trips and assign unique trip IDs.
- Enforce valid state transitions (REQUESTED → DRIVER_ASSIGNED → IN_PROGRESS → COMPLETED → PAID → RATED).
- Persist trips in a relational database (for strong consistency and queries).
- Emit lifecycle events for other services (Dispatch, Pricing, Payment, Analytics).
Minimal trip schema (SQL)
CREATE TABLE trips (
id UUID PRIMARY KEY,
rider_id UUID NOT NULL,
driver_id UUID NULL,
pickup_lat DOUBLE PRECISION NOT NULL,
pickup_lng DOUBLE PRECISION NOT NULL,
dropoff_lat DOUBLE PRECISION NOT NULL,
dropoff_lng DOUBLE PRECISION NOT NULL,
product VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL, -- REQUESTED, IN_PROGRESS, ...
estimated_fare NUMERIC(10,2),
final_fare NUMERIC(10,2),
surge_multiplier NUMERIC(4,2),
created_at TIMESTAMP NOT NULL,
started_at TIMESTAMP NULL,
completed_at TIMESTAMP NULL
);
CREATE INDEX idx_trips_rider ON trips(rider_id, created_at DESC);
CREATE INDEX idx_trips_driver ON trips(driver_id, created_at DESC);
CREATE INDEX idx_trips_status ON trips(status, created_at);
- Each row describes one trip: who the rider and driver are, where the trip starts and ends, what product was used, and what the current status is.
- estimated_fare holds the pre-trip estimate, while final_fare is set once the trip completes and Pricing Service returns its calculation.
- Timestamps allow Trip Service and analytics to compute durations, recency, and historical patterns. Indexes support common queries like “recent trips for rider X” and “active trips for driver Y”.
Key APIs (high level)
POST /trips -> CreateTrip
POST /trips/{id}/assign -> AssignDriver
POST /trips/{id}/start -> StartTrip
POST /trips/{id}/end -> CompleteTrip
POST /trips/{id}/rate -> RateTrip
GET /trips/{id} -> GetTrip
GET /riders/{id}/trips -> ListTripsForRider
GET /drivers/{id}/trips-> ListTripsForDriver
Each API is invoked behind the API gateway; Trip Service ensures the requested state transition is valid and persists the change before emitting an event.
Dispatch Service: matching riders and drivers
Dispatch is the matching brain of the system. It decides which driver should serve which trip request.
Responsibilities
- Listen for trip.requested events from Trip Service.
- Query Location Service for nearby available drivers.
- Filter drivers based on availability, product compatibility, and reliability.
- Rank candidates using distance, ETA, rating, and acceptance probability.
- Send offers to selected drivers and handle accept/decline.
From trip requested to driver assigned

- When a trip is created, Trip Service emits a trip.requested event on the message bus.
- Dispatch Service subscribes to this topic and receives new ride requests.3–4. It calls Location Service to get a list of nearby drivers that are currently available and have the right vehicle type.
- It filters and ranks candidates using a scoring function or ML model (described in more detail in a later algorithm-focused article).6–7. The top candidate(s) receive an offer via Notification Service; Bob sees a “new trip” request on his phone.
- If Bob accepts, Dispatch calls a Trip Service API to assign the driver and updates the trip status; Trip Service then emits trip.driver_assigned for others to react.
Location Service: real-time driver positions
Location Service powers the moving cars on the map and “drivers near me” features.
Responsibilities
- Accept GPS updates from driver apps.
- Maintain a fast geospatial index of available drivers.
- Support queries like “drivers within 2 km of this point, for product X”.
- Provide real-time streams of driver location to riders during a trip.
Location write and read paths

- The driver app sends frequent location updates; through the gateway, these reach Location Service.
- Location Service updates a geospatial index (often Redis GEO, geohash, or a similar structure) so queries like “drivers near this point” are fast.
- It also logs locations in a time-series store for audits, analytics, and ML, but those writes are optimized for high throughput.
- Dispatch uses a single “query nearby drivers” API and does not care about the underlying indexing method, which can evolve over time.
Pricing Service: estimates and final fares
Pricing decides how much a ride costs and communicates that to riders and drivers.
Responsibilities
- Provide quick fare estimates before a trip is booked.
- Compute final fares using actual time, distance, and surge multipliers.
- Apply discounts, coupons, and taxes where relevant.
Simple fare model (for illustration)
Base fare = base_fee
Distance fare = per_km_rate * distance_km
Time fare = per_minute_rate * duration_minutes
Raw fare = base fare + distance fare + time fare
Surge fare = Raw fare * surge_multiplier
Final fare = Surge fare + taxes - discounts
Details like how surge is computed and how dynamic pricing evolves are covered later in the ML/algorithms article, but this model is enough to see how Pricing interacts with other services.
Key APIs (logical)
POST /pricing/estimate
-> inputs: pickup, dropoff, product, time
-> outputs: estimated_fare, estimated_distance, estimated_duration, surge_multiplier
POST /pricing/finalize
-> inputs: trip_id, actual_distance, actual_duration, surge_snapshot
-> outputs: final_fare_breakdown
Trip Service calls estimate before creation (or through the gateway), and finalize after the trip is completed to compute the actual fare.
Payment Service: charging riders and paying drivers
Payment Service turns completed trips into money movements.
Responsibilities
- Consume trip.completed events.
- Charge riders via external payment providers (cards, wallets, etc.).
- Record payment results and update trip status to PAID.
- Emit payout events for driver earnings.
From completed trip to payment

- Trip Service publishes trip.completed when the driver ends the ride and Pricing has finalized the fare.
- Payment Service listens for that event, looks up the rider’s payment method, and makes a call to an external payment provider (PSP).
- The result is stored as a Payment row and, on success, trip.paid is emitted so Trip Service and Driver Service can update their state (trip status, driver earnings).
Payment flows must be idempotent: retrying a payment should not double-charge, which usually involves idempotency keys and careful handling of PSP responses (details for a future article).
User/Driver and Notification Services
These services support everything above and are used by many other components.
User and Driver Services
- Store profile data (name, phone, email), verification status, and preferences.
- Store ratings and derived stats (average rating, acceptance rate, cancellation rate).
- Provide quick lookups so other services do not duplicate this data.
Example minimal driver table:
CREATE TABLE drivers (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
vehicle_type VARCHAR(32) NOT NULL,
rating NUMERIC(3,2),
total_trips INT,
is_available BOOLEAN,
last_online_at TIMESTAMP
);
CREATE INDEX idx_drivers_available ON drivers(is_available, last_online_at);
Notification Service
- Sends push notifications and in-app messages based on events from Trip, Dispatch, Payment, etc.
- Supports channels like push (FCM/APNs), in-app banners, and possibly SMS for important events.
Examples:
- “Driver assigned” to rider.
- “New trip offer” to driver.
- “Trip started/ended”.
- “Payment successful” and “receipt ready”.
Notification Service usually subscribes to events and uses templates plus user preferences (language, notification settings) to build messages.
Bringing it together: one ride through core services
To close, here is a short flow that touches all core services at a higher level.
Summary flow
- Rider App calls Trip Service (via gateway) to create a trip. Trip Service writes to DB and emits trip.requested.
- Dispatch Service consumes trip.requested, uses Location Service to find nearby drivers, and sends an offer to the best candidate via Notification Service.
- Driver accepts; Dispatch calls Trip Service to assign driver, and Trip Service emits trip.driver_assigned so Notification Service can update the rider.
- While the trip is active, Driver App sends locations to Location Service; Rider App receives updates to draw the live route.
- When the trip ends, Trip Service updates status to COMPLETED and calls Pricing Service to compute final fare; after that, it emits trip.completed.
- Payment Service consumes trip.completed, charges the rider, and emits trip.paid, which updates trip status and driver earnings; Notification Service informs both parties.
This division into focused services keeps responsibilities clear and lets each part scale and evolve independently.
Conclusion
This article walked through the core backend services in an Uber‑like system: Trip, Dispatch, Location, Pricing, Payment, User/Driver, and Notification. It showed how Trip Service owns the lifecycle and data of each ride, while Dispatch, Location, and Pricing work together to match riders and drivers and compute fair prices, and Payment and Notification complete the loop with charges and updates. By combining diagrams and plain-language flows, the article made the responsibilities and interactions of each service in the ride sharing architecture concrete and easier to reason about. In the next article, the series will zoom into data and storage design—covering relational schemas, time-series location storage, caching strategies, and how to choose the right database patterns for an Uber-scale backend.





