ADR-018: Typed Analytics (GA) Architecture

Prev Next

Context

Analytics within the CMS was previously implemented as loosely typed GA event calls with string-based event names and unstructured payloads. This approach increases the risk of inconsistent naming, silent schema drift, payload fragmentation, and long-term dashboard instability.

As the CMS grows in domain complexity (content, monetization, app configuration, admin), analytics events become part of a contract that influences reporting, operational visibility, and product insights. Without structure and enforcement, event proliferation and inconsistency would lead to unreliable analytics data and long-term technical debt.

A decision was required to establish a clear, typed, and scalable analytics architecture that enforces event consistency while remaining flexible enough to evolve with the system.

Decision

We will implement a typed analytics architecture for GA based on:

  • A strongly typed AnalyticsEventEnvelope as the canonical event contract.
  • Category-to-event mapping enforced via CategoryEventMap for compile-time narrowing.
  • Action-based event names (e.g., login, upload_complete) with outcome modeled as an optional dimension (success | fail | cancel), rather than encoding outcome in event names.
  • A centralized schema_version (GA config) representing the analytics payload contract version.
  • A dedicated normalization adapter (normalizeAnalyticsPayload.adapter.ts) responsible for:
    • Removing undefined values
    • Truncating long strings
    • Joining arrays
    • Enforcing GA parameter limits
  • A private GA transport layer with a public trackAnalytics method.
  • Production-only event emission to prevent data pollution in GA.

Event names sent to GA follow the ${category}_${event} pattern.

Consequences

Enables

  • Compile-time enforcement of valid category → event combinations.
  • Consistent, versioned analytics payload structure.
  • Safer evolution of analytics schema over time.
  • Cleaner GA dashboards through predictable naming.
  • Isolation of transport concerns from domain modeling.
  • A structured telemetry contract within the frontend.

Restricts

  • All analytics events must conform to the envelope structure.
  • Category and event additions require explicit typing.
  • Breaking schema changes require conscious schema_version increment.

Trade-offs Accepted

  • Slightly increased upfront type complexity in exchange for long-term consistency.
  • An additional adapter layer for normalization before GA transport.
  • Centralized control over analytics emission rather than ad-hoc GA calls.

Notes

  • schema_version should only be incremented for breaking payload structure changes that affect interpretation of historical data.
  • Per-event payload strict typing will be implemented in a future iteration.
  • The analytics envelope design intentionally avoids embedding outcome in event names to reduce event explosion and improve aggregation.
  • While the envelope is structured and PII-free by design, this ADR governs GA analytics usage specifically.