Skip to content

[RFC] Extract shared geo.utils.ts — consolidate 3 Haversine duplications #4

@Khoa-Dam

Description

@Khoa-Dam

Problem

Haversine distance and grid math are duplicated across 3 services:

  • notifications/notifications.service.tsprivate distanceKm() method
  • gateways/crime-reports.gateway.ts — inline grid cell math
  • crime-reports/crime-reports.service.tsradiusKm / 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRequest for Comments — architecture proposals

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions