All articlesShoaib
Case study//7 min read

Building a Real-Time Geospatial Monitoring Platform

The architecture decisions that make live operational data useful instead of merely fast.

Real-time systemsGeospatialArchitecture
Building a Real-Time Geospatial Monitoring Platform

The decision

A map is not a system design. For a monitoring product, the hard problem is deciding which events matter, how fresh they need to be, and what the operator should do when data arrives late or out of order.

The useful design starts with an event contract: a stable identity, an observation time, a source time, and enough context to explain why the event is on screen.

What changed

I would separate ingestion, normalization, storage, and presentation. The map consumes a read model shaped for the operator rather than querying raw telemetry directly. That keeps rendering concerns away from device and transport concerns.

The trade-off is deliberate duplication. A small amount of derived state buys predictable reads, easier replay, and a place to attach alert rules without turning the frontend into a second backend.

What I would measure

Freshness, dropped events, replay duration, and the time between an alert appearing and an operator acknowledging it matter more than raw message throughput. A live system earns trust through explainability, not just speed.

A useful mental model

The platform has four distinct responsibilities. Devices produce observations, the ingestion layer accepts and authenticates them, the domain layer decides what those observations mean, and the read model presents a coherent view to operators. Collapsing those responsibilities into one handler makes the first demo quick and the second month painful.

flowchart LR
  A[Devices and sensors] --> B[Ingestion API]
  B --> C[Validation and normalization]
  C --> D[(Event log)]
  D --> E[Domain rules and alerts]
  D --> F[Operator read model]
  E --> F
  F --> G[Map and timeline UI]

Handling imperfect data

Telemetry is frequently late, duplicated, or reported with a clock that is not synchronized. I would store both the time the device observed an event and the time the platform received it. The first answers operational questions; the second helps diagnose transport health.

Every event also needs a stable identity. A consumer can then discard a duplicate without guessing whether two identical coordinates represent two observations or one message delivered twice. For out-of-order events, the UI should update the current state only when the incoming version is newer, while retaining the event in history for audit and replay.

type PositionEvent = {
  eventId: string;
  assetId: string;
  observedAt: string;
  receivedAt: string;
  sequence: number;
  latitude: number;
  longitude: number;
};

function shouldUpdate(current: PositionEvent | undefined, incoming: PositionEvent) {
  return !current || incoming.sequence > current.sequence;
}

What an operator needs

The map should not be the only source of truth. A selected asset needs a recent-event timeline, freshness indicator, last known status, and a clear explanation when its position is stale. Alert thresholds should be visible and versioned, because changing a rule changes how historical alerts are interpreted.

Trade-offs

An event log and a derived read model introduce storage and replay work. I would accept that cost when the product needs auditability, multiple views, or recovery after a consumer outage. For a small internal tool, a direct query may be enough. The architecture should follow the operational consequence of being wrong, not the appeal of a streaming diagram.