Maps, routing, and ETA
- Place the map provider behind an internal facade and justify it.
- Design caching for geocoding and ETA calls so vendor cost and latency stay off the critical path.
From 0.2 we buy maps. From 0.3, quotes hit 5,000/s at peak — and every quote needs an ETA. Naively that's 5,000 vendor calls/s: ruinous in dollars and a hard availability coupling to someone else's SLA.
Every internal consumer (pricing, matching, trip tracking) calls our own Geo facade, never the vendor directly. The facade gives us three things: a single choke point for caching and rate limiting, vendor independence (swap or dual-source providers without touching business logic), and a place to degrade gracefully — a cached, slightly stale ETA beats a failed quote.
flowchart LR
PRICE["Pricing service"] --> GEO
MATCH["Matching service"] --> GEO
TRIP["Trip service"] --> GEO
subgraph GEO ["Geo facade"]
API["Geo API"]
C1[("Geocode cache — address to coords, TTL days")]
C2[("Route cache — cell-pair to ETA, TTL 2 min")]
H3["Cell grid — snap coords to ~150 m hex cells"]
API --> H3
API --> C1
API --> C2
end
API -- "cache miss only" --> VEND["🗺️ Map vendor — routes, ETA, geocoding"]
The load-bearing trick is quantizing space. Raw coordinates are nearly unique, so raw ETA lookups never hit cache. Snap both endpoints to a hexagonal grid cell (~150 m across) and cache cell-pair → travel time for two minutes, and cache hit rates in dense cities exceed 90% — turning 5,000 quote ETAs/s into a few hundred vendor calls/s. Hex grids reappear as the unit of surge pricing (1.5), dispatch search (1.6), and geo-sharding (2.5); this lesson is your pre-training on them.
Cell-granular ETAs are fine for quotes (an estimate) but not for navigation or final fare calculation. The driver app navigates with the vendor SDK directly; the final fare uses the actual recorded route (1.7). Know which precision each consumer needs — over-precision is money, under-precision is disputes.