Reverse Geocoding – Technical Details
Back to DemoHigh-Level System Overview
- Purpose: Resolve WGS84 latitude/longitude input into structured address and place metadata using ArcGIS
reverseGeocode. - Business problem: Demonstrates low-latency geospatial lookup suitable for map-click workflows, field operations, asset lookup, and location validation.
- Architecture: Razor Page and ArcGIS MapView → versioned ASP.NET Core API →
IReverseGeocodingService→ grid-snapped distributed cache → ArcGIS REST API. - Technologies: .NET 10, Razor Pages, ArcGIS JavaScript SDK, typed
HttpClient, Polly resilience,IDistributedCache, Redis or in-memory fallback. - Deployment model: Runs inside the Portfolio web application on Render and can scale horizontally with Redis sharing cached coordinate results across replicas.
Frontend Architecture
The front end uses a Razor Page with wwwroot/js/ReverseGeocoding/app.js and ArcGIS JavaScript SDK map interactions. State is intentionally lightweight: the selected coordinate, current lookup result, and recent lookup history are managed in browser JavaScript.
- Map interaction: Map click events provide coordinates that are sent to the backend API; users can also enter coordinates manually.
- API communication:
api-config.jsdefines/api/v1/geocoding/reverse, and shared fetch helpers provide consistent cookie and error behavior. - State management: Lookup history is page-local, avoiding server persistence for demo interactions that do not require identity.
- UX decisions: The page separates raw coordinate input from structured returned address components, making geocoding accuracy and location type easy to discuss.
API Layer
- Controller:
ReverseGeocodingControlleris versioned at/api/v1/geocoding/reverseand is[AllowAnonymous]. - Endpoint:
GET /api/v1/geocoding/reverse?lat={lat}&lng={lng}returnsReverseGeocodingResultDto. - Validation: The service validates latitude in −90..90 and longitude in −180..180, throwing
ArgumentExceptionfor invalid coordinates. - Error handling: Bad coordinates return 400, no usable ArcGIS address returns 404, and an open circuit breaker returns 503 with retry guidance.
- Security: The endpoint is read-only, accepts numeric query values only, and does not require or mutate user identity.
Services and Business Logic
- Service boundary:
ReverseGeocodingServiceowns coordinate validation, grid snapping, cache key creation, ArcGIS request construction, response handling, and DTO mapping. - Geospatial optimization: Coordinates are snapped to
ReverseGeocoding:GridResolutionDegreesbefore cache lookup so nearby clicks reuse a single cached result. - External integration: A typed
HttpClientcalls ArcGISreverseGeocodethrough timeout, retry, and circuit breaker policies. - Mapping: ArcGIS wire models live in
Portfolio.Common/ArcGis;MapToDtoprefersStAddrwithAddressfallback because ArcGIS field population varies by address type. - Service contract: Public service methods accept
CancellationToken, return DTOs, and throw typed exceptions for controller translation.
Data Access Layer
Reverse geocoding does not require relational persistence. Its state is cache-oriented because the expensive operation is an upstream geospatial lookup, not an internal database query.
- Caching strategy: Results are serialized with shared JSON options into
IDistributedCacheusing a sliding expiration configured byReverseGeocoding:CacheSlidingExpirationMinutes. - Scale-out behavior: Redis lets multiple app replicas share snapped-coordinate cache entries; local development falls back to
MemoryDistributedCachewhenRedis:ConnectionStringis empty. - Query optimization: Grid snapping trades exact per-click precision for dramatically better cache hit rates in dense click workflows.
- Database role: PostgreSQL remains available for other domains, but this workflow avoids unnecessary writes for ephemeral lookup results.
Infrastructure and Deployment
- Hosting: Deployed with the main Portfolio application on Render; configuration is provided through
appsettingsand environment variables. - Distributed cache:
Redis__ConnectionStringswitches the app from in-process cache to Redis-backedIDistributedCachefor reverse-geocode results and other shared geocoding workflows. - Shared platform role: The same Redis instance also supports batch geocoding job state and production Data Protection keys for multi-replica cookie validity.
- Reliability: Polly protects the app and ArcGIS dependency from long waits, transient failures, and repeated calls during upstream instability.
- Containers: Docker Compose includes Redis for local cache behavior close to production; Kubernetes manifests support replica scaling behind a service.
- Cloud architecture: In Azure, this maps cleanly to Azure Container Apps or AKS plus Azure Cache for Redis and managed PostgreSQL while preserving the current service API.
Engineering Decisions and Tradeoffs
- Grid-snapped cache: Improves latency and reduces ArcGIS calls, but may return a nearby address instead of a unique result for every raw coordinate.
- Anonymous access: Reduces friction for a public geospatial demo, but production usage would typically add quotas, API keys, or authenticated tenant limits.
- No persistence: Keeps the workflow stateless and scalable, but removes historical lookup analytics unless observability or audit storage is added.
- Future improvements: Add rate limiting, precision tiers, cache warming for known assets, telemetry on cache hit ratio, and region-aware upstream failover.
Interview Discussion Points
- How would you choose grid resolution for urban, rural, and parcel-level workflows?
- What metrics would prove the cache is reducing upstream geocoding cost?
- How should a public reverse-geocoding API enforce fair usage without hurting UX?
- Where would you add distributed tracing across browser, API, cache, and ArcGIS?
- How would this design change if reverse geocoding were part of a high-volume mobile fleet system?