# How DataLayerManager Manages Data Layers in God's Eye View: Architecture & Lifecycle

> Discover how DataLayerManager orchestrates real-time data overlays in God's Eye View. Learn about its architecture and lifecycle for managing CesiumJS data layers.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: architecture
- Published: 2026-09-09

---

**The DataLayerManager in bilawalsidhu/gods-eye-view is a centralized orchestrator that registers, toggles, and refreshes real-time data overlays on a CesiumJS globe using an intent-based state machine with deterministic epoch tracking.**

The `DataLayerManager` serves as the control plane for all data visualization layers in God's Eye View. Located in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js), this class bridges the gap between raw data modules and the CesiumJS viewer, handling everything from initial registration to periodic refresh cycles. By separating absolute intent from relative toggles, the manager ensures predictable layer behavior even during rapid user interactions or network failures.

## Core Architecture and State Management

At initialization, the `DataLayerManager` establishes a private `Map` structure to track layer entries by their stable string `id`. Each entry maintains the layer's enabled state, lifecycle status, refresh timers, and intent bookkeeping.

### Construction and the Layer Registry

The manager requires a Cesium `viewer` reference upon instantiation. According to the source code in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) (lines 18–26), the constructor initializes:

- A `viewer` reference for scene integration
- A `_layers` Map storing `id → {module, ...}` mappings
- Internal tracking for QA-specific registrations

```javascript
import { DataLayerManager } from './data/manager.js';

// Instantiate with the Cesium viewer
const manager = new DataLayerManager(viewer);

```

### Layer Registration and Validation

The `register(layerModule)` method (lines 31–38) validates that incoming modules provide a stable string `id`. The manager rejects duplicate IDs and throws errors if registration occurs after `finalizeRegistrations()` has sealed the registry.

For development environments, `registerForQa()` (lines 39–47) allows injection of synthetic test layers when the manager is constructed with `{allowQaRegistration: true}`. These QA layer IDs are tracked separately in `_qaLayerIds` for cleanup isolation.

```javascript
import trafficLayer from './data/traffic.js';

// Register production layers
manager.register(trafficLayer);

// Seal registrations and validate serialization disposition
manager.finalizeRegistrations([
  { id: trafficLayer.id, disposition: 'enabled-only' }
]);

```

The `finalizeRegistrations()` method ensures every layer has a defined serialization disposition (e.g., `'enabled-only'` or `'enabled+options'`), preventing accidental runtime additions that could destabilize the visualization state.

## Intent-Based Visibility Control

The manager implements a sophisticated **visibility intent model** using an epoch counter (`visibilityIntentEpoch`) to guarantee deterministic ordering of state changes.

### Absolute vs. Relative State Changes

**`setEnabled(layerId, shouldEnable, ...)`** invokes the private `_setEnabledWithIntent` method (lines 74–82) to execute a full lifecycle: optional `init`, `enable`, immediate `update`, then periodic refresh scheduling. Each absolute request increments the epoch and stores an intent record, ensuring newer intents supersede stale requests even when rapid toggles occur.

**`toggle(layerId, {...})`** flips the effective state by calling `_setEnabledWithIntent` with `!isEffectivelyEnabled`. This method also supports a `notifyWillChangeBeforeEffective` flag (lines 40–55) that emits a "will-change" event before the actual transition, allowing UI components to prepare for state shifts.

```javascript
// Absolute enable (intent-based)
await manager.setEnabled('traffic', true, { origin: 'user' });

// Relative toggle (swaps current state)
await manager.toggle('satellites');

```

### Lifecycle State Presentation

The manager maintains explicit `lifecycleState` values: `'enabled'`, `'disabled'`, `'enabling'`, or `'disabling'`. Through `_setLifecycleTransition` and `_syncModuleLifecyclePresentation` (lines 62–71), these states propagate to layer modules implementing `setLifecyclePresentation`, keeping UI toggles synchronized with underlying data fetching operations.

## Refresh Loops and Error Resilience

When a layer specifies a `refreshInterval` or `updateInterval` greater than zero, `_armUpdateLoop` creates a `setInterval` that triggers `_runPeriodicUpdate` (lines 22–31). This loop respects:
- Current enabled state (aborting if disabled)
- Abort signals for in-flight requests
- Automatic cancellation upon layer destruction

Errors during any lifecycle step (`init`, `enable`, `update`) are captured centrally. Rather than hiding data unintentionally, the manager may retain layers in an **uncertain** state, preserving the last known good visualization while logging failures for debugging.

## Event Notification and Subscription

All state transitions broadcast through an internal listener set (`_listeners`). Consumers subscribe via `manager.subscribe(callback)` and receive notifications for:
- `visibility-will-change`
- `visibility` updates
- Refresh completions
- Error conditions

After each successful update, the manager triggers `governorRequestRender` to force a Cesium render pass and marks detection sources as changed (lines 101–108).

```javascript
const unsubscribe = manager.subscribe(change => {
  console.log('Layer change:', change.layerId, change.newState);
});

// Cleanup subscription
unsubscribe();

```

## Practical Implementation Examples

### Manual Refresh and Camera Synchronization

Force immediate data refresh before camera movements or screenshot capture:

```javascript
// Trigger immediate update bypassing the interval
await manager.refreshLayer('traffic');

```

### Working with Production Layer Modules

Concrete layer implementations like [`src/data/traffic.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/traffic.js) and [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js) expose standard interfaces (`init`, `enable`, `update`, `disable`) that the manager orchestrates. The [`src/data/localLayers.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/localLayers.js) file lists default production layers registered at application startup.

```javascript
import { DataLayerManager } from './data/manager.js';
import trafficLayer from './data/traffic.js';
import satellitesLayer from './data/satellites.js';

const manager = new DataLayerManager(viewer);
manager.register(trafficLayer);
manager.register(satellitesLayer);
manager.finalizeRegistrations([
  { id: trafficLayer.id, disposition: 'enabled+options' },
  { id: satellitesLayer.id, disposition: 'enabled+mirrored-options' }
]);

```

## Summary

- **Centralized Registration**: The `DataLayerManager` in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) maintains a strict registry of layer modules with validation via `finalizeRegistrations()`.
- **Deterministic State Machine**: Uses `visibilityIntentEpoch` to handle rapid toggle sequences without race conditions.
- **Lifecycle Orchestration**: Manages complete layer lifecycles from `init` through `enable`, periodic `update`, and `disable`.
- **Fault Tolerance**: Captures errors at each lifecycle stage and supports "uncertain" states to prevent data loss.
- **Event-Driven Architecture**: Provides subscription-based notifications for all visibility and refresh changes.

## Frequently Asked Questions

### What is the difference between `toggle()` and `setEnabled()` in DataLayerManager?

**`setEnabled()`** makes an absolute state request (explicitly on or off) and increments the intent epoch to ensure the request takes precedence over concurrent operations. **`toggle()`** queries the current effective state and requests the opposite, making it ideal for checkbox or button interactions where the current state may be unknown to the caller.

### How does DataLayerManager handle rapid visibility changes?

The manager implements an **intent epoch** counter (`visibilityIntentEpoch`) in `_setEnabledWithIntent` (lines 74–90). Each visibility request receives a monotonically increasing epoch number. If multiple requests fire in rapid succession, only the highest epoch (most recent intent) executes, preventing visual flicker and inconsistent data states.

### Why is `finalizeRegistrations()` required before enabling layers?

This method seals the layer registry and validates that each layer has a serialization disposition (e.g., `'enabled-only'`). It prevents accidental runtime registrations that could destabilize the serialization state or introduce untracked layers during production use, ensuring the manager maintains a deterministic, serializable configuration.

### How are refresh intervals managed for active layers?

When a layer module specifies `refreshInterval` or `updateInterval`, `_armUpdateLoop` creates a `setInterval` timer that calls `_runPeriodicUpdate`. The loop automatically canels if the layer disables, the viewer destroys, or an abort signal fires, preventing memory leaks and unnecessary network requests.