# Understanding the Lifecycle Contract for Data Layers in God's Eye View

> Learn about the lifecycle contract for data layers in God's Eye View. Discover how the DataLayerManager ensures deterministic state transitions through registration, initialization, refresh, and teardown.

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

---

**God's Eye View treats every real-time overlay as a data layer managed by the `DataLayerManager` in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js), enforcing a deterministic state machine that requires modules to expose specific async methods and return boolean promises to transition through registration, initialization, enabling, periodic refreshes, and teardown.**

The `bilawalsidhu/gods-eye-view` repository implements a rigorous lifecycle contract to ensure reliable real-time data visualization. This contract governs how overlay modules integrate with the core viewer, handling everything from initial asset loading to graceful degradation when network conditions fluctuate.

## Core Architecture and State Management

At the heart of the system lies the `DataLayerManager` class, which maintains an internal map of registered layers and tracks their progression through discrete lifecycle states: `disabled`, `enabling`, `enabled`, and `disabling`. The manager also maintains a `lifecycleUncertain` flag to handle failure scenarios where cleanup operations fail.

The contract demands that every layer module expose a stable string `id` property. Beyond this identifier, modules may optionally implement `init`, `enable`, `update`, `disable`, `destroy`, and `getStats` methods. All async methods must return boolean promises—`true` signals successful transition, while `false` or thrown exceptions trigger error handling protocols.

## Phase 1: Registration and Validation

Registration occurs when `manager.register(layerModule)` is invoked. The manager validates that the module provides a unique `id` string and checks for collisions in the internal registry. No initialization occurs during this phase; the manager simply catalogs the module's capabilities for subsequent lifecycle operations.

## Phase 2: Initialization

When `manager.setEnabled(layerId, true)` is first called, the manager transitions the layer to the `enabling` state and invokes `module.init(viewer, {signal})`. This method must complete any asynchronous startup tasks, such as fetching GeoJSON assets or establishing WebSocket connections.

According to the source in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js), if `init` returns `false` or throws an exception, the enable operation aborts immediately, preventing partial initialization states.

## Phase 3: Enable and First Update

Following successful initialization, the manager calls `module.enable(viewer, {signal})`. This method should activate rendering primitives and attach data sources. Only after the promise resolves to `true` does the manager set `entry.enabled` and transition to the `enabled` state.

Immediately upon enabling, the manager performs a mandatory `module.update(viewer, {signal})` call. This guarantees that data are present before the first render cycle, preventing empty or flickering overlays.

## Phase 4: Periodic Refresh

If a layer declares a `refreshInterval` or `updateInterval` property, the manager arms a `setInterval` timer that repeatedly invokes `module.update`. Each tick is wrapped in the internal `_runPeriodicUpdate` method, which emits `refresh-transition`, `refresh`, or `refresh-failed` events to registered listeners.

The `update` method must be safe to call repeatedly and should return `false` to signal a rejected refresh attempt, allowing the manager to update the feed state accordingly without crashing the manager itself.

## Phase 5: Disable and Cleanup

When visibility is toggled off via `manager.setEnabled(layerId, false)`, the manager calls `module.disable(viewer, {signal})`. This method must clean up resources and hide primitives. If `disable` returns `false` or throws, the manager sets `lifecycleUncertain = true` and keeps the refresh interval alive—a fail-closed strategy that prevents resource leaks while signaling the degraded state to the UI.

## Phase 6: Destroy and Teardown

The final phase invokes `module.destroy(viewer)` when `destroyLayer(layerId)` is called or during internal cleanup. This optional method performs final tear-down, after which the manager removes the entry from its internal map. No further calls are made to the module after destruction.

## Error Handling and Intent Epochs

The manager implements robust error isolation through custom error types defined in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) lines 33-43. Exceptions from lifecycle methods are caught and converted to `LifecycleRejectedError` or `LayerParamsRejectedError` instances. These are recorded in `entry.managerRefreshError` and surfaced via `visibility-failed` or `refresh-failed` events.

To handle rapid visibility toggles deterministically, the manager implements an **intent epoch** system (lines 84-132). Every absolute visibility change creates a new epoch; superseded intents are cancelled with `superseded-visibility-intent` reasons. This guarantees that only the most recent request prevails, preventing race conditions in asynchronous operations.

## Feed State and Health Monitoring

The helper function `layerFeedState(stats)` (lines 72-112) normalizes heterogeneous `getStats()` output into six UI-friendly labels: `nominal`, `loading`, `degraded`, `stale`, `fallback`, and `unavailable`. Layers may optionally implement `getStats()` to return diagnostic objects containing `status`, `error`, `count`, and `lastUpdate` fields, enabling the UI to reflect real-time health metrics.

## Implementation Example

The following example demonstrates a minimal compliant data layer implementation:

```javascript
// src/data/exampleLayer.js
export const id = 'example';
export const refreshInterval = 5000;   // Manager polls every 5s

export async function init(viewer, { signal }) {
  const resp = await fetch('https://example.com/data.geojson', { signal });
  const geojson = await resp.json();
  viewer.scene.primitives.add(Cesium.GeoJsonDataSource.load(geojson));
  return true;
}

export async function enable(viewer, { signal }) {
  viewer.scene.primitives.show = true;
  return true;
}

export async function update(viewer, { signal }) {
  // Refresh data; return false to reject this refresh cycle
  return true;
}

export async function disable(viewer, { signal }) {
  viewer.scene.primitives.show = false;
  return true;
}

export function getStats() {
  return { status: 'ok', count: 42, lastUpdate: Date.now() };
}

```

To register and control this layer:

```javascript
import { DataLayerManager } from './src/data/manager.js';
import * as exampleLayer from './src/data/exampleLayer.js';

const manager = new DataLayerManager(viewer);
manager.register(exampleLayer);
await manager.setEnabled('example', true);    // init → enable → first update
await manager.refreshLayer('example');        // manual refresh
await manager.setEnabled('example', false');  // disable

```

## Summary

- The **DataLayerManager** in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) orchestrates a deterministic state machine for all overlay modules.
- Layers must expose a unique `id` string and optionally implement `init`, `enable`, `update`, `disable`, `destroy`, and `getStats` methods.
- All lifecycle methods return boolean promises; failures trigger `LifecycleRejectedError` and set `lifecycleUncertain` flags without crashing the manager.
- The **intent epoch** system (lines 84-132) guarantees deterministic resolution of rapid visibility toggles by cancelling obsolete work.
- Periodic refreshes are manager-owned; layers simply implement `update` and may declare `refreshInterval` for automatic polling.
- Health states are normalized through `layerFeedState` into six categories: `nominal`, `loading`, `degraded`, `stale`, `fallback`, and `unavailable`.

## Frequently Asked Questions

### What methods must a data layer implement to satisfy the lifecycle contract?

A layer must expose a stable string `id` property. All other methods—`init`, `enable`, `update`, `disable`, `destroy`, and `getStats`—are optional but recommended for full functionality. The manager checks for method existence before invocation, allowing minimal implementations that only handle registration and manual updates.

### How does the manager handle errors thrown during lifecycle transitions?

Any exception from lifecycle methods is caught in [`src/data/manager.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/manager.js) and converted to a `LifecycleRejectedError` (lines 33-37) or `LayerParamsRejectedError` (lines 39-43). The manager records the error in `entry.managerRefreshError`, emits a `visibility-failed` or `refresh-failed` event, and sets the `lifecycleUncertain` flag. This fail-safe approach prevents manager crashes while surfacing degradation states to the UI through the feed state system.

### Why does the manager keep the refresh interval alive if disable() fails?

This fail-closed strategy prevents resource leaks when cleanup operations fail. If `disable()` returns `false` or throws, the manager sets `lifecycleUncertain = true` and maintains the `setInterval` rather than risking orphaned timers or unreleased memory. The layer remains in a known, trackable state until manual intervention or a subsequent successful disable operation.

### How can I implement manual refresh controls for a data layer?

While the manager automatically calls `update()` at `refreshInterval` if declared, you can trigger manual refreshes via `manager.refreshLayer(layerId)`. This method invokes the same `update(viewer, {signal})` method used in periodic refreshes, allowing users to force data synchronization on demand without waiting for the next interval tick.