Metadata & Geography — How it works
Overview
The metadata engine owns the platform's shared reference/lookup data — principally the administrative division tree. It is centrally seeded, read-mostly data that other engines reference by id: the demographic engine validates a person's address against it, and the facility engine anchors facilities in it for geographic access scoping.
Depth is data, not schema. Each country declares its own ordered, self-named rungs, so Nigeria
is State → LGA → Ward and the UK is Region → ICS → Place → PCN, with no country privileged by the
model. This replaced a fixed country → state → county → city → community chain whose five levels
were hardcoded as NOT NULL columns on facility, person_address, encounter and every generated
form table — a shape that forced every country to have a state and could not represent the UK at all.
Data model & ownership
| Table | Purpose |
|---|---|
country | Owns a set of levels. Not itself a rung: it is the partition key, and what tenant.country_id points at. |
division_level | One rung of one country's hierarchy: ordinal, level_name, level_code. Unique per (country, ordinal) and (country, name). |
division | A node in that country's tree. Self-referencing via parent_id; carries depth and a materialized division_path. |
country.min_facility_depth says how deep a facility must sit in that country. Nigeria is 3 (Ward).
Primary keys: these are the deliberate exception to the platform's UUID rule — because they are
seeded centrally and never created concurrently across nodes, they use stable numeric
(Long/Integer) surrogate keys. See the architecture overview.
Key rules & invariants
division_pathis a materialized ancestor path including the node's own id —/1/38/812/. A subtree is one indexed prefix scan (division_path LIKE '/1/38/%'), so access-scope resolution needs no hierarchy walk. The trailing separator matters: it is what stops/1/matching/12/. Indexed withvarchar_pattern_opsso the prefix match is a range scan under any collation.depth,division_pathanddivision_level_idare server-derived and never caller-writable. The mappers deliberately do not copy them. A caller able to supply its own path could graft a node into another authority's subtree and inherit that authority's grants.- A division can be renamed but not re-parented. Moving it would invalidate the stored path of every descendant, and of every facility, address and grant referencing them.
- Precision is depth, never nullability. A facility must reach its country's
min_facility_depth; an address may name any rung, so one known only as far as its LGA points at the LGA rather than inventing a ward.division_idis NOT NULL on facility, person_address and encounter alike — the flexibility is in which node, not in whether there is one. - Sibling names are unique per parent, not per country: six Nigerian LGA names genuinely repeat across states (Bassa, Ifelodun, Irepodun, Nasarawa, Obi, Surulere).
Nigeria's seed
Seeded from the FMoH Master Facility List: 37 states, 774 LGAs, 10,463 wards — 11,274 divisions,
with 40,463 facilities all attached at ward level. States and LGAs are derived from facility_code
rather than the workbooks' name columns, because the code's first two segments yield exactly the
official 37 and 774 while the name columns yield 785 pairs (196 rows sit in the wrong state's
workbook). Regenerate with scripts/mfl-import/import_mfl.py; do not hand-edit the CSVs.
404 LGAs whose register rows named no ward carry a placeholder ward named
"<LGA> - ward not recorded", identifiable by division_code LIKE '%/00'. They exist so every
facility reaches ward level and a drill-down cannot silently show an empty LGA; the name says what
they are so they are never mistaken for surveyed wards.
The other seeded countries
All 195 UN-recognised states are seeded (193 members plus the Holy See and Palestine). Four of them declare division levels, and divisions are loaded only where the list can be stated in full:
| Country | Rungs | Divisions loaded | min_facility_depth |
|---|---|---|---|
| United Kingdom | Nation → Health Board → Locality | 4 nations, all 68 health boards | 2 |
| United States | State → County | 51 states + DC, 394 counties across 18 states | 1 |
| Canada | Province or Territory → Health Region | 13, plus 26 health regions in 8 of them | 1 |
| Australia | State or Territory → Local Hospital Network | 8, plus 53 LHNs in 7 of them | 1 |
The UK's 68 are England's 42 Integrated Care Boards, Scotland's 14 NHS Boards, Wales' 7 Health Boards and Northern Ireland's 5 HSC Trusts — the statutory tier in each nation.
A parent is either loaded completely or left empty, never partially. A state showing 12 of its 67 counties looks exactly like a state that has 12, and someone entering an address would silently fail to find their own. Which case applies is recorded rather than implied:
division.children_complete is true when every real-world child of that node has been loaded. It
exists so a picker can say "not loaded yet" instead of presenting an empty list as if the division
genuinely had no children. Nigeria's states and LGAs and the UK's nations are true; 33 US states,
5 Canadian provinces and Victoria are false.
The gaps are deliberate. The remaining ~2,750 US counties are too many to state without error;
Alberta, Quebec, Newfoundland and Labrador, the Northwest Territories and Yukon are all
mid-reorganisation, so any list written today would be wrong within the year; and Victoria's ~76
public health services cannot be enumerated reliably. Load them from an authoritative file the way
Nigeria's were, via scripts/mfl-import — and raise min_facility_depth when the tier a facility
actually sits at is present.
API
See the API Reference. Under /api/v1/metadata:
| Endpoint | Purpose |
|---|---|
/countries | Countries. |
/division-levels?countryId= | A country's rungs, shallowest first. Clients read this first — it says how many selects to render and what to label them. |
/divisions?countryId= | That country's top-level divisions. |
/divisions?parentId= | One node's children — a drill-down step. |
/divisions?countryId=&depth= | Every division on one rung. |
/divisions/{id}/subtree | A division and everything beneath it. |
One resource serves every rung of every country, replacing the separate /states, /counties,
/cities and /communities endpoints.
Configuration & feature flags
None.
Related features
- Demographic (address validation), Facility (geography roll-up), Access control (geographic scopes).