ExpertGeospatialMatchingConcurrencyStreamingPricing

Ride Hailing Platform

Match riders and drivers under location churn and acceptance races.

Product overview

Match riders with eligible nearby drivers, maintain a trustworthy trip state machine, and price and settle completed transportation under high location churn.

Main actors

Rider, Driver, Safety operator, Support.

Assumptions

  • Drivers are independently available or unavailable
  • A maps provider supplies routes
  • Pricing rules are versioned
  • Safety and licensing obligations vary by jurisdiction

Non-goals

  • Autonomous driving
  • Building maps
  • Replicating any proprietary dispatch algorithm
  • Cash handling

Requirements

Must-have

  • Driver availability and location ingestion
  • Expiring fare quote
  • Race-safe matching
  • Ordered trip transitions
  • Final fare and receipt

Should-have

  • Scheduled rides
  • Safety escalation
  • Driver destination filters
  • Cancellation fees

Future scope

  • Shared rides
  • Multi-region roaming
  • Fleet accounts
  • Demand prediction

Non-functional decisions

ConcernDecision
LatencyCandidate lookup and offer delivery are interactive; final settlement can converge asynchronously.
ConsistencyA trip has one assigned driver and ordered states. Location, ETA, and heat maps are eventual.
AvailabilityNever create ambiguous assignment to improve availability; degrade maps and pricing explicitly.
PrivacyExact trip location is purpose-limited, access-logged, and retained according to policy.
AuditabilityPrice rule/version, route inputs, safety access, and manual changes are preserved.

User roles

RoleAccess boundary
RiderOwn quotes, trips, receipts
DriverOwn availability, offers, assigned trips, earnings
Safety operatorBreak-glass trip access with immutable audit
SupportMasked trip and payment view; scoped adjustments

Core workflows

Request ride
Validate pickup/drop-off → Create expiring quote → Authorize payment method → Create requested trip → Find eligible drivers → Issue staged offers → Atomically assign first winner
Take trip
Driver arrives → Rider verifies trip PIN where required → Start trip → Stream coarse progress → Complete at actual destination → Calculate final fare → Capture and settle

Domain model

EntitiesRelationship
Driver → DriverStatusAvailability is durable; positions are ephemeral
RideQuote → PricingVersionQuote fixes inputs and expiry
Trip → DriverOfferMany offers, no more than one winner
Trip → FareFinal fare records components and quote relationship
Trip → SafetyIncidentRestricted incident record and audit trail

Database design

PostgreSQL remains the transactional source of truth. Add specialized stores only for the workloads called out below.

trips — Authoritative ride workflow

trips ----- id UUID PK rider_id UUID FK driver_id UUID FK NULL status TEXT pickup GEOGRAPHY dropoff GEOGRAPHY quote_id UUID FK version INT Constraints: one active assignment per trip; valid status check Indexes: (rider_id, created_at DESC); (driver_id, status)

driver_status — Durable eligibility state

driver_status ------------- driver_id UUID PK state TEXT vehicle_id UUID FK region_id UUID version INT Constraints: one state per driver Indexes: (region_id, state)

ride_quotes — Expiring price promise

ride_quotes ----------- id UUID PK rider_id UUID FK pricing_version TEXT amount_minor BIGINT currency CHAR(3) expires_at TIMESTAMPTZ route_hash TEXT Constraints: amount_minor >= 0 Indexes: (rider_id, expires_at)

driver_offers — Dispatch attempts

driver_offers ------------- id UUID PK trip_id UUID FK driver_id UUID FK status TEXT expires_at TIMESTAMPTZ Constraints: UNIQUE(trip_id, driver_id) Indexes: (driver_id, status, expires_at)

Transaction and retention boundaries

  • Accept offer by locking/conditionally updating both trip and driver availability
  • Trip transition plus outbox is atomic
  • Final fare record and balanced earnings entries share a commit
  • Ephemeral raw locations expire; financial/trip audit records use retention states, not ad-hoc delete

API design

POST /v1/ride-quotes POST /v1/trips POST /v1/driver-offers/:id/accept PATCH /v1/trips/:id/status POST /v1/drivers/me/locations GET /v1/trips/:id
EndpointPurpose and permissionIdempotency / status
POST /v1/ride-quotesPrice a route with an expiry
Rider
Not required
201, 422, 503
POST /v1/tripsRequest a ride from a quote
Rider
Required
201, 409, 422
POST /v1/driver-offers/:id/acceptAttempt trip assignment
Offer recipient
Required
200, 409, 410
PATCH /v1/trips/:id/statusAdvance allowed state with version
Assigned driver or restricted operator
Transition id
200, 409, 422
POST /v1/drivers/me/locationsIngest sequenced location batch
Authenticated eligible driver
Not required
202, 400, 429
GET /v1/trips/:idRead authorized trip projection
Trip rider/driver or audited staff
Not required
200, 403, 404

List endpoints use opaque keyset cursors unless a small bounded collection is explicit. Unknown objects return 404 where 403 would leak existence.

Architecture

System context

Ride Hailing Platform system context
Ride Hailing Platform system context

Main application architecture

Ride Hailing Platform application architecture
Ride Hailing Platform application architecture

Critical workflow sequence

Ride Hailing Platform workflow sequence
Ride Hailing Platform workflow sequence

Event model

EventProducer → consumersDelivery
TripRequestedTrip API → MatcherOutbox; matcher deduplicates trip/version
DriverAssignedMatcher → Realtime, notificationsAt-least-once; versioned projection
TripCompletedTrip API → Fare, settlement, safety analyticsInbox dedupe; settlement dead-letter is operationally visible

Failure modes

Acceptance race

Failure
Two drivers accept near-simultaneously.

Mitigation
Conditional trip and driver updates in one transaction; only committed winner succeeds.

Stale location

Failure
Nearest candidate is no longer nearby.

Mitigation
Filter by sample age, show offer expiry, and expand search rings.

Out-of-order GPS

Failure
Mobile batches arrive late.

Mitigation
Per-device sequence/timestamp rejects older live projection while retaining bounded telemetry.

Completion timeout

Failure
Trip completes but response is lost.

Mitigation
Transition id returns stored outcome; fare creation is unique per trip.

Maps outage

Failure
No route or ETA is available.

Mitigation
Stop new quotes or use clearly bounded fallback policy; active trips remain operable.

Driver disappears

Failure
Assigned driver goes offline before pickup.

Mitigation
Heartbeat timeout marks attention-needed; safe reassignment uses explicit cancellation transition.

Consistency and concurrency

Strong consistency / transactions

  • One trip-driver assignment
  • Driver cannot hold incompatible active trips
  • State ordering
  • Fare and earnings

Eventual convergence

  • Position
  • ETA
  • supply heat map
  • notifications

Concurrency controls

  • Trip assignment is a database compare-and-set, not a distributed lock
  • Lock trip then driver in stable order
  • Unique active-trip constraint protects driver eligibility
  • Monotonic location sequence controls reordering

Caching and background work

Safe to cache

  • Map tiles
  • Pricing configuration by version
  • Coarse supply heat map

Never a cache authority

  • Assignment winner
  • Current trip state for writes
  • Safety authorization

Versioned config is long-lived; heat maps use seconds-long TTL. Live reads include freshness and writes always hit the owner.

Background jobs

  • Expire quotes/offers
  • Expand match radius
  • Detect missing heartbeats
  • Calculate fare
  • Reconcile capture
  • Apply location retention

Security boundaries

Security claims here are design controls, not certification. Threat modeling and jurisdiction-specific review remain required.

  • Trip object authorization for rider and assigned driver
  • Short-lived scoped driver device tokens
  • Minimize exact location exposure and audit staff access
  • Server-verifiable state transitions and anti-spoofing signals
  • Rate limits on location and ride requests
  • Signed payment webhooks and hosted tokenization
  • Step-up access for safety tooling

Observability

Logs

  • Trip and driver state transitions with actor, expected version, and rejection reason
  • Match attempt ring, candidate count, offer expiry, and assignment winner
  • Location ingestion sequence gaps and dropped stale samples without logging precise coordinates

Metrics

  • Quote latency, match latency, and unassigned-trip age by city
  • Offer acceptance, assignment conflict, and driver cancellation rates
  • Driver location freshness and ingestion rejection rate
  • Illegal trip-transition attempts and fare reconciliation exceptions

Traces

  • Trace ride request from quote through geo lookup, offer delivery, and assignment CAS
  • Trace trip completion through fare calculation, capture, and earnings posting

Alerts

  • Match latency or unassigned backlog breaches city SLO
  • Fresh eligible-driver supply drops unexpectedly
  • Completed trips remain unpriced or uncaptured

Testing strategy

LayerWhat it proves
UnitTrip state machine, quote expiry, pricing rules, sequence handling
IntegrationPostGIS lookup, atomic assignment, outbox, fare uniqueness
ContractMap/payment adapters and mobile event versions
ConcurrencyDriver/trip cross-races and duplicate transitions
FailureOffline devices, delayed GPS, map timeout, capture ambiguity
LoadGeographically skewed location writes and stadium dispatch burst

Do not mock PostgreSQL constraints, transaction isolation, object-store semantics, or provider contract fixtures in the tests intended to prove those boundaries.

Deployment evolution

Stage 1
Modular API, PostgreSQL/PostGIS, Redis ephemeral geo index, queue, realtime gateway
Stage 2
Dedicated location ingestion and matching workers by city; API replicas; analytical stream
Stage 3
Region-owned trip/matching cells with controlled roaming; separate financial ledger boundary

Scale evolution

These are architecture scenarios, not throughput claims. Each transition should follow measured workload and reliability pressure.

ScenarioArchitecture may evolve towardDecision rule
1K usersOne API, PostgreSQL with PostGIS for trips, an in-memory geo index of available drivers, a matching workerAssign a driver with a conditional update; stale driver positions are expected, double assignment is not
100K usersSeparate high-write location path; shard matcher by city/geocell; keep trip writes on PostgreSQL ownerShard matching by city or geocell when match latency or location freshness degrades; each trip keeps a single owner
1M+ usersRegional cells own matching and trips; event streams feed cross-region analytics without making them assignment authoritiesA regional cell owns matching and trip state for its area; cross-region streams feed analytics, never assignment