backend phase 4: geography, addresses & nurse service areas
Adds the province -> city -> district reference hierarchy (geo schema, seeded with 31 provinces + capital cities + Tehran's 22 districts), nurse service areas (district_id NULL = whole city, filtered-index-pair uniqueness -> 409), and encrypted, geocoded customer addresses with a single-primary invariant. Introduces the IGeocoder seam (mocked) and 409 Conflict on the result envelope. Public cascading lookups are cached behind a generation-token scheme with invalidate-on-admin-write. One EF migration (GeographyAddressesServiceAreas, applied). Contract + swagger snapshot + handoff/report/registry updated. 103 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,3 +7,13 @@
|
||||
|
||||
### `nurse_service_areas` [CORE]
|
||||
**Role:** Where a nurse will travel. A row with `district_id = NULL` means the entire city. **Why a join table (not a radius):** Iranian nurses think in named districts, not GPS radii; this also drives the geographic filter in search cheaply. Unchanged, with `UNIQUE(nurse_id, city_id, district_id)`. **Relations:** N:1 → `nurse_profiles`, `cities`, `districts`.
|
||||
|
||||
> **As-built (backend-phase-4):** the geo hierarchy lives in a **`geo` schema** (`Provinces`/`Cities`/
|
||||
> `Districts`/`NurseServiceAreas`), seeded via `HasData` with all 31 provinces, each province's capital
|
||||
> city, and Tehran's 22 مناطق. Because SQL Server treats NULLs as distinct in a unique index, the
|
||||
> whole-city (`district_id = NULL`) uniqueness is enforced with a **filtered-index pair** —
|
||||
> `UNIQUE(nurse_id, city_id) WHERE district_id IS NULL AND deleted_at IS NULL` **plus**
|
||||
> `UNIQUE(nurse_id, city_id, district_id) WHERE district_id IS NOT NULL AND deleted_at IS NULL` — so both a
|
||||
> duplicate whole-city row and a duplicate city+district row are rejected (surfaced as `409`), while a
|
||||
> soft-removed area can be re-declared. Public lookups are cached and filter `is_active` at every level
|
||||
> (a deactivated parent hides its children); geocoding is behind the mocked **`IGeocoder`** seam.
|
||||
|
||||
Reference in New Issue
Block a user