Problem
Haversine distance and grid math are duplicated across 3 services:
notifications/notifications.service.ts — private distanceKm() method
gateways/crime-reports.gateway.ts — inline grid cell math
crime-reports/crime-reports.service.ts — radiusKm / 111 literal approximation
Each copy has slightly different precision or approach. A bug in one doesn't get fixed in the others.
Proposed Solution — Pure functions module
New file: src/shared/geo.utils.ts
/** Haversine great-circle distance in km */
export function distanceKm(lat1: number, lng1: number, lat2: number, lng2: number): number
/** Stable grid cell key at ~11km resolution */
export function gridCellKey(lat: number, lng: number): string
/** 3×3 neighbourhood of grid keys around a point */
export function adjacentGridKeys(lat: number, lng: number): string[]
/** Approximate km → degrees conversion (km / 111) */
export function kmToDegrees(km: number): number
No NestJS DI — pure functions, imported directly.
Consumers to migrate
| File |
Action |
src/notifications/notifications.service.ts |
Delete private distanceKm(), import from geo.utils |
src/gateways/crime-reports.gateway.ts |
Replace inline grid math with gridCellKey / adjacentGridKeys |
src/crime-reports/crime-reports.service.ts |
Replace radiusKm / 111 literal with kmToDegrees(radiusKm) |
Benefits
- One fix propagates to all consumers
- Pure functions are trivially unit-testable (no DB, no DI container)
- Gateway grid logic becomes readable and maintainable
References
Architecture RFC session — 2026-04-12
Problem
Haversine distance and grid math are duplicated across 3 services:
notifications/notifications.service.ts—private distanceKm()methodgateways/crime-reports.gateway.ts— inline grid cell mathcrime-reports/crime-reports.service.ts—radiusKm / 111literal approximationEach copy has slightly different precision or approach. A bug in one doesn't get fixed in the others.
Proposed Solution — Pure functions module
New file:
src/shared/geo.utils.tsNo NestJS DI — pure functions, imported directly.
Consumers to migrate
src/notifications/notifications.service.tsprivate distanceKm(), import fromgeo.utilssrc/gateways/crime-reports.gateway.tsgridCellKey/adjacentGridKeyssrc/crime-reports/crime-reports.service.tsradiusKm / 111literal withkmToDegrees(radiusKm)Benefits
References
Architecture RFC session — 2026-04-12