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
AnalyticsEventEnvelopeas the canonical event contract. - Category-to-event mapping enforced via
CategoryEventMapfor compile-time narrowing. - Action-based event names (e.g.,
login,upload_complete) withoutcomemodeled 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
trackAnalyticsmethod. - 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_versionincrement.
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_versionshould 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.