Geo Features and Flows
Complete feature list, actor journeys, state flows, business rules, edge cases, and diagrams for the Geo module.
Use this page for the geo reference domain: what it does for consumers and systems, and how the reads behave.
| Source Type | Files or Docs | What Was Extracted |
|---|
| API | apps/api/src/modules/geo/customer/geo-customer.controller.ts | Routes, rate limits, pagination behaviour |
| Backend | apps/api/src/modules/geo/customer/geo-customer.service.ts | Ordering, province filter, pagination |
| Schema | packages/db/src/schema/geo/{province,district,municipality,enums}.ts | Tables, codes, the empty municipality reference |
| Seed | packages/db/src/seed/seed-geo.ts | Idempotent seeding, db:seed:geo |
| Error registry | apps/api/src/common/types/error-codes.ts (// GEO) | GEO_PROVINCE_NOT_FOUND, GEO_DISTRICT_NOT_FOUND |
| Field | Value |
|---|
| Module | geo |
| Submodule | N/A |
| Primary user value | Authoritative, code-stable province and district reference data for every location picker |
| Actors | Guest (any caller), internal systems via GeoLookupService |
| Main entry points | GET /api/mobile/geo/provinces, GET /api/mobile/geo/districts, GeoLookupService |
| Main outputs | Province/district lists; resolved district rows for FK conversion |
| Related docs | API, Backend |
| Actor | Can Do | Cannot Do | Auth Requirement | Notes |
|---|
| Guest / any caller | List provinces, list districts (all or by province) | — | None (@Public()) | PUBLIC_READ 60/min per IP |
| Internal systems | Resolve a district uuid → district row via GeoLookupService | Read geo tables directly | Internal service | Single FK-conversion path |
| Admin | — | — | — | No admin surface; data is seeded, not edited |
| Capability | Surface | Actor | Route/Trigger | State Read | State Written | Linked API Section |
|---|
| List provinces | Public | Guest | GET /api/mobile/geo/provinces | province | — | API |
| List districts | Public | Guest | GET /api/mobile/geo/districts | district | — | API |
| Filter districts by province | Public | Guest | ?provinceId= | district | — | API |
| Resolve district for FK | Internal | Any module | GeoLookupService.resolveDistrict | district | — | backend §6 |
A guest (or customer) opens an address form. The client fetches provinces, then districts for the selected province. The picker keys on code and displays name.
| Branch | Condition | Behavior | Error/Result |
|---|
| Unknown province | provinceId names nothing | 404 | GEO_PROVINCE_NOT_FOUND |
| Unknown district (lookup path) | uuid names nothing | 404 | GEO_DISTRICT_NOT_FOUND |
| Pagination on | pagination=true | Offset pages with count/currentPage/totalPage | — |
| Renamed province | Government renames | code unchanged, name updates | Consumers keyed on code unaffected |
Called by shipping (quote), address (create/update) and any future module: GeoLookupService converts a district uuid into the internal integer id — the single place that conversion happens.
None. Geo has no admin surface and no configuration: the reference data is government-published and seeded, not edited through the API.
None — reference data has no lifecycle. The only transition is the seed itself: an empty database gains provinces and districts, idempotently, via db:seed:geo.
| Flow | DB Writes | Cache Effects | Jobs | Realtime | Analytics | Notifications |
|---|
| List provinces/districts | — | — | — | — | — | — |
Seed (db:seed:geo) | province, district | — | — | — | — | — |
| Scenario | Trigger | User/System Experience | Recovery | Source |
|---|
| Rate limited | > 60 requests/min from one IP | 429 | Retry later | PUBLIC_READ |
| Unknown id | Bad provinceId/district uuid | 404 with code | Refresh the picker list | service |
| Seed re-run | Environment re-seeded | No-op on existing rows | Idempotent | seed script |
- Actor capability diagram — §3/§4.
- Sequence diagram per major flow — §5.1.
- Data side-effect diagram — §9.
| Feature | Minor Behavior | Actor | Trigger | User/System Result | Backend Side Effect | Source |
|---|
| Provinces | Official ordering | Guest | list | displayOrder order | — | service |
| Provinces | Rename resilience | Guest | renames | code stable | — | schema comment |
| Districts | Name ordering | Guest | list | alphabetical | — | service |
| Districts | Province filter | Guest | ?provinceId | Subset | — | service |
| Districts | Merge traps | Guest | rukum-east vs rukum-west | Distinct rows, different provinces | — | seed |
| Lookup | Unknown id | System | resolve | 404 | — | service |
| Rule | Business Reason | Actor Impact | Enforced In | API Impact | Backend Impact | Tests |
|---|
Key on code, not name | Provinces get renamed | Consumers survive renames | Schema + docs | code in every response | — | seed |
| Four split-district codes | Third-party datasets merge them | Correct picker rows | Seed data | Distinct codes | — | seed |
| Single FK-conversion path | One true resolution | Modules never read geo directly | GeoLookupService | — | Service boundary | — |
| Public reads throttled | Load, not secrecy | 429 possible | PUBLIC_READ | — | guard | — |
| Product Decision | User Benefit | Engineering Benefit | Alternative | Tradeoff | Risk |
|---|
| Municipality reference empty | Name is free text | No 753-row memory game | Full municipality table | No municipality ids | Documented |
| Public, unauthenticated | Picker works pre-login | No auth plumbing | Authenticated reads | Load | Rate limited |
| Seeded, not editable | Canonical data | No drift | Admin editing | Seed-only updates | Idempotent re-seed |
| Flow | Edge Case | Trigger | Expected Behavior | User/System Feedback | Source |
|---|
| Provinces | Empty database | Fresh environment | Empty until seeded | [] | seed ordering |
| Districts | Unknown province id | Bad query | 404 | GEO_PROVINCE_NOT_FOUND | |
| Districts | Pagination | pagination=true | Offset pages | Envelope metadata | |
| Lookup | District renamed | Reference update | Code stable | Same id | |
| Flow | Reads | Writes | Cache | Jobs/Events | Response Fields |
|---|
| Provinces | province | — | — | — | id, code, name, displayOrder |
| Districts | district (+ province) | — | — | — | id, code, name, province |