Feb 2026 • 5 min read

Map Core Architecture

Building a reusable geospatial foundation for operational transport systems.

Context

Central Facility was already a successful, map-heavy operational product before GDS. It shipped on Leaflet and showed that geography on screen could anchor real operations—not a decorative panel off to the side. GeoJSON rendering, custom markers, status colouring, hover and selection, directional cues, relationships between entities: all of it carried ordinary product semantics, expressed spatially rather than as list rows alone.

When GDS started, more than one downstream surface looked likely to need that class of tooling. GDS landed on MapLibre mainly for breadth in rendering pipelines and customised feature drawing as layers and overlays became denser—not as a pronouncement against what Central Facility shipped, but because the newer programme needed more manoeuvring room in the renderer.

The aim was pragmatic platform work: settle a shared geospatial spine rather than reinvent map stacks beside every product boundary. Central Facility had already validated the usefulness of map-heavy operations UIs—we needed agreement on behaviours, not another debate about whether maps belonged.

What I started noticing

The painful part stopped being “can we plot this polygon.” The harder stretch was preserving interaction rhythms, expectations, and sensible data handling whenever users crossed from one geography-centric screen to the next—a pattern we already glimpsed operating Central Facility, only now multiplied across prospective consumers.

Problems repeated in familiar shapes: layers whose visibility chased domain predicates in divergent ways, hover and selection that felt different despite similar labels on the legend, inconsistent treatment of nested or heterogeneous GeoJSON, relationship lines between entities reimplemented subtly per app. Domain shortcuts beside the renderer became routine whenever delivery pressure hit.

Screens that belonged to different crews still surfaced the same tacit norms—similar colours and affordances ought to behave the same way. When they diverged, “the map” caught the blame even when the breakage was behavioural inconsistency layered on acceptable geometry.

The approach that worked better

Separate the reusable engine from the transport-shaped product wiring.

  • Map Core: Reusable and domain agnostic—lifecycle management, view state, layer stacking, rendering orchestration, interaction patterns reused across audiences. Lane logic, disruptions, berth naming, fleet overlays: anything transport-specific stays out.
  • Map Kit: Built on Core, consumes GDS data, expresses transport behaviours, exposes basemap options tuned so road-network context reads cleanly next to overlays, and holds integrations that assume that dataset exists.

Architecture attention went to interaction vocabulary first—hover, focus, ephemeral highlights, quieted states—and to firm seams between orchestration helpers and predicates that belonged with business code. That sequencing made rendering strategies easier to swap when performance or styling demanded it without rewriting downstream workflows wholesale.

Why this mattered

  • Operators see map surfaces that behave like siblings: toggles, focus, hints.
  • Frontend teams avoided rebuilding the interaction substrate repeatedly.
  • Orchestration stays in Core while business predicates and payloads stay nearer Kit or the host app.
  • Lifecycle and interaction tweaks ship once per Core release instead of forking silently per viewer.
  • GDS quirks stay encapsulated in Kit instead of weakening shared primitives under deadline pressure.

Trade-offs

  • Abstraction adds another layer when you debug—you often walk Core’s APIs before touching the renderer.
  • Shared behaviour needs discipline across teams; slipping one-off hacks through erodes trust in the spine.
  • Not every surface needs this mould; simple dashboards or ornamental maps owe nothing to the full stack.
  • If boundaries fray, Core either bloates toward useless generality or quietly absorbs domain details until reuse stops meaning anything.

What I would keep doing

Hold Core to a slim set of stable primitives: framing the view, ordering layers, lifecycle hooks, outbound events, the shared vocabulary for gestures and transient states—then insist transport particulars live in Kit or the host product. Carry those primitives with the seriousness you reserve for routing and auth ergonomics rather than pretending the canvas is interchangeable chrome wedged beside other widgets.

Related Notes

  • MobX Architecture Patterns
  • Map Data Contracts