Admin Boundaries
Overview
Section titled “Overview”An admin boundary is a polygon that defines an administrative area — a country, region, county, city, or district. They sit alongside Market Boundaries but classify by political / administrative geography rather than by market sector.
Every address gets auto-linked to the admin boundaries it sits inside. Editing a boundary’s polygon re-runs the spatial sync against every affected address — addresses that have moved into the boundary gain a link, those that have moved out lose theirs.
Admin boundaries are managed by Admins only — non-admin users can see them on list views (one combined “Admin Boundaries” column per list) but can’t edit the boundaries themselves.
Required fields
Section titled “Required fields”| Field | Required when | What you see if missing |
|---|---|---|
| Code | Always | ”Code is required.” |
| Name | Always | ”Name is required.” |
| Type | Always | A boundary must reference an existing Admin Boundary Type (Country, Region, County, etc.). |
| Tier | Always | A boundary must reference a Tier Type that determines which list column it shows in. |
The polygon is optional at save time — you can create a boundary with metadata only and add the polygon later. Boundaries without a polygon are skipped during the address auto-sync (they don’t contribute to any address’s boundary set until the shape is filled in).
Field-level rules
Section titled “Field-level rules”Code max 50 characters
Section titled “Code max 50 characters”The form rejects codes longer than 50 characters. The code must be unique across all admin boundaries (the DB enforces a unique index).
Name max 255 characters
Section titled “Name max 255 characters”The form rejects names longer than 255 characters.
Description max 500 characters
Section titled “Description max 500 characters”Optional free text.
Where this lives: src/services/web/src/lib/validation/admin-boundary.ts.
Geometry rules
Section titled “Geometry rules”The polygon is run through the same validation pipeline as Market Boundaries. The rules are:
- Must be a single polygon (no MultiPolygon).
- At least 4 points (a closed ring needs 4 distinct coordinates with the first and last matching).
- Non-zero area.
- Every coordinate within WGS84 range (longitude −180 to 180, latitude −90 to 90).
- Topologically valid (no self-intersections; rings closed properly).
- At most 10,000 vertices. The editor surfaces an informational message above 2,500 vertices, a stronger warning above 5,000, and disables Save above 10,000.
The shared validation service handles both entity types — failure messages reference AdminBoundary.Polygon rather than MarketBoundary.Polygon for clarity in error responses.
Where this lives: src/common/services/Validation/GeometryValidationService.cs (validity checks) and src/services/api/app/app.api/Handlers/Commands/AdminBoundaries/AdminBoundaryGeometryValidator.cs (the prefix shim).
Tier-based hierarchy
Section titled “Tier-based hierarchy”Every admin boundary belongs to a Tier Type that says where it sits in the administrative hierarchy. The seeded defaults are:
| Tier | Name | Typical examples |
|---|---|---|
| 1 | Country | UK, US, France |
| 2 | Region | England, California, Île-de-France |
| 3 | County | Greater London, Cook County, Hauts-de-Seine |
| 4 | City | London, Chicago, Paris |
| 5 | District | City of London, The Loop, 1er Arrondissement |
Tiers are seeded by the DACPAC but admins can add more via the Tier Types admin area. New tiers show up automatically — the list pages render a single denormalised “Admin Boundaries” string column (the boundary names, ordered by tier, comma-separated), so adding a tier doesn’t require a frontend change.
On save
Section titled “On save”Required-field check
Section titled “Required-field check”Code, Name, Type, Tier all re-checked server-side.
Polygon validation
Section titled “Polygon validation”The server runs the polygon through the validation service. If a polygon is supplied, every rule above is enforced.
Concurrency check
Section titled “Concurrency check”The save carries the boundary’s Version integer. If another admin has updated the boundary since you loaded it, you get a concurrency error — reload and re-apply your changes. (Unlike most other entities, admin boundaries use an int Version rather than a RowVersion byte array — same effect, less standard wire shape; flagged for future hardening.)
After save
Section titled “After save”Address auto-sync
Section titled “Address auto-sync”The geometry-changed event fires after every Create / Patch / Replace / Delete that touched the polygon. For each affected address (those inside the old polygon, the new polygon, or both), the spatial-intersection sync re-evaluates which admin boundaries the address sits inside and updates AddressAdminBoundary accordingly. The sync picks the leaf-most boundary per tier — if an address sits inside both “England” (tier 2) and “Greater Manchester” (a more specific tier 2 sub-area), only the more specific one is linked.
The same sync also runs whenever an address’s own Location changes (on Create / Patch / Replace of the address). Clearing an address’s Location removes every AddressAdminBoundary link for that address.
Where this lives: src/services/api/app/app.api/Services/Model/AddressAdminBoundaryService.cs + src/services/api/app/app.api/Handlers/Events/AdminBoundaries/AdminBoundaryGeometryChangedEventHandler.cs.
Where admin boundaries appear
Section titled “Where admin boundaries appear”- One “Admin Boundaries” column on the Address, Scheme, Company, Occupier Event, and Investment Event list pages — names ordered by tier (Country → Region → County → City → District) and comma-separated, mirroring the existing Markets column on Addresses.
- An admin area at /admin-boundaries for managing boundaries (full polygon editor coming in a subsequent release; in the meantime, manage via the OData / write API).
- Admin areas for the underlying type and tier dictionaries at /admin-boundary-types and /tier-types.
Related rules
Section titled “Related rules”- Market Boundaries — same shape, different classification axis (market sector vs administrative geography).
- Addresses — the spatial-sync rule runs on every address write.
- Concurrent edits — admin boundaries use
int Versionfor concurrency rather thanRowVersion, but the user-visible behaviour matches. - Permissions and roles — boundary edits are admin-gated.
Where this lives
Section titled “Where this lives”- SQL schema:
src/data/app/app/Tables/AdminBoundary.sql,AddressAdminBoundary.sql,AdminBoundaryType.sql - Seed defaults:
src/data/app/Scripts/SeedAdminBoundaryTypes.sql,SeedTierTypes.sql - EF model:
src/common/models/Models/AdminBoundary.cs - Controllers:
src/services/api/app/app.api/Controllers/AdminBoundariesODataController.cs,AdminBoundariesWriteController.cs - Command handlers:
src/services/api/app/app.api/Handlers/Commands/AdminBoundaries/ - Sync service:
src/services/api/app/app.api/Services/Model/AddressAdminBoundaryService.cs - Frontend types + config:
src/services/web/src/lib/types/common.ts,src/services/web/src/lib/odata/configs/admin-boundaries.svelte.ts - List-view mappers: any
*ListViewMapper.csthat callsAdminBoundaryTierMapper.FromAddress(...)