Role: Analytics Solutions Expert
Scope: Ford Digital Commerce Monorepo — all apps, all markets, all teams
Audience: Senior Architects, Engineering Leadership, Vendor Partners
Document type: HLD — LLD to follow separately
Last updated: 2026-06-12
Before picking a solution, we must understand that we have four distinct problems that need four distinct solutions. Most failed analytics migrations fail because they try to solve all four with one tool.
┌─────────────────────────────────────────────────────────────────────────┐
│ FOUR PROBLEMS, FOUR SOLUTIONS │
├────────────────────┬────────────────────────────────────────────────────┤
│ PROBLEM LAYER │ WHAT IT MEANS │
├────────────────────┼────────────────────────────────────────────────────┤
│ 1. CONTRACT │ No single source of truth for event definitions. │
│ │ Teams interpret PV differently. │
│ │ Solution: Schema Registry + Contract System │
├────────────────────┼────────────────────────────────────────────────────┤
│ 2. RUNTIME │ 5 subsystems, 2 paths, global mutable state. │
│ │ No validation before send. │
│ │ Solution: Unified SDK + Managed Data Layer │
├────────────────────┼────────────────────────────────────────────────────┤
│ 3. TENANT │ Market/brand/country variation hardcoded in source. │
│ │ New market = code change + deployment. │
│ │ Solution: Policy Engine + Tenant Overlay System │
├────────────────────┼────────────────────────────────────────────────────┤
│ 4. GOVERNANCE │ Manual PV → code. No approval process. │
│ │ Ownership distributed, no contract lifecycle. │
│ │ Solution: PV Compiler + Governance Pipeline │
└────────────────────┴────────────────────────────────────────────────────┘
We analyzed five distinct architectural approaches. Each solves the problem from a different angle. These are real patterns used by Adobe, Netflix, Segment, Tealium, Snowplow, mParticle, and Google.
What it is:
Build an internal analytics SDK that behaves exactly like a commercial vendor SDK.
Teams treat it as a black box. They call typed functions. The platform handles everything else.
This is what Netflix, Airbnb, and Spotify have done internally.
Core idea:
Developer calls: analytics.track("PRODUCT_ADDED_TO_CART", { sku, price, quantity })
Platform does: validate → enrich → tenant-resolve → dispatch → observe
Developer sees: nothing else. The contract is enforced silently.
How it works:
track(), page(), identify() functionswindow.digitaldata or _satellite directlyWhat it solves:
Pros:
Cons:
Effort: High
Value: Very High
Time to first value: 8–12 weeks for MVP
Best for: Teams that want full control, TypeScript integration, long-term ownership
What it is:
Keep Adobe Satellite as the dispatcher. Fix the data layer management instead of replacing it.
Introduce a structured, managed data layer interface based on the W3C CEDDL (Customer Experience Digital Data Layer) standard.
This is what large enterprise Adobe customers like Ford have done — fix the container, not the vendor.
Core idea:
Instead of: window.digitaldata = { ... } ← raw mutation
Use: DataLayer.push({ event, data }) ← managed interface
How it works:
DataLayer manager class that owns window.digitaldataDataLayer.push() with event + data envelopewindow.digitaldata writes anywhereWhat it solves:
Pros:
Cons:
Effort: Medium
Value: Medium-High
Time to first value: 4–6 weeks
Best for: Teams that want minimal code change, Adobe-centric approach
What it is:
Introduce a lightweight in-browser event bus.
All domains publish events to the bus with typed payloads.
One central consumer subscribes and dispatches to Adobe.
This decouples publishers (feature teams) from consumers (Adobe, GA4, Meta, etc.).
This is the pattern used by companies that support multiple analytics vendors simultaneously.
Core idea:
FSC publishes: EventBus.emit("FSC:PAYMENT_SELECTED", payload)
NVC publishes: EventBus.emit("NVC:FILTER_CLEARED", payload)
NaBuy publishes: EventBus.emit("NABUY:CHECKOUT_STARTED", payload)
One consumer: EventBus.on("*", (event) => {
validate(event)
resolveToAdobeContract(event)
dispatch(event)
})
How it works:
AnalyticsEventBus package in the monorepoAnalyticsConsumer subscribes to all eventsWhat it solves:
Pros:
Cons:
Effort: Medium
Value: High
Time to first value: 6–8 weeks
Best for: Multi-vendor analytics strategy, teams that want vendor flexibility
What it is:
Define all events as machine-readable schemas first (JSON Schema / TypeScript / YAML).
Use code generation to produce TypeScript types, Zod validators, and documentation automatically.
PV authors work in a structured format (not free Excel). Code is generated, not handwritten.
This is what companies like Segment (Protocols), Amplitude (Govern), and Snowplow (Iglu) have built as commercial products.
Core idea:
PV Author writes: event-catalog.json
Generator runs: generates TypeScript types + Zod schemas + docs
Developer uses: import { FILTER_CLEARED_EVENT } from "@analytics/contracts"
CI enforces: payload must satisfy FILTER_CLEARED_EVENT schema before merge
How it works:
What it solves:
Pros:
Cons:
Effort: Medium-High
Value: Very High
Time to first value: 6–10 weeks
Best for: Scaling to many teams, eliminating human error in PV translation, long-term
What it is:
Not a chatbot for end users.
An AI intelligence layer embedded in the development and CI/CD pipeline.
It reads PV documents, generates contracts, validates implementations, detects drift, and explains errors.
Think: GitHub Copilot but for analytics governance — a member of your platform team, not a user-facing product.
This is NOT:
This IS:
Core idea:
PV Document (Excel)
↓ AI reads and understands
Generated Contract (TypeScript + JSON Schema)
↓ Developer implements
PR opened
↓ AI validates implementation vs contract
↓ AI explains any mismatch in plain English
Merge approved
↓ E2E runs
↓ AI compares expected vs actual payload
↓ AI identifies root cause if mismatch
Deployed
↓ AI monitors live payload quality
↓ AI alerts on schema violations with tenant context
What it solves:
Pros:
Cons:
Effort: High (foundation) + Medium (AI layer)
Value: Extremely High long-term
Time to first value: 12–16 weeks
Best for: Scaling governance, eliminating PV translation errors, long-term platform excellence
After analyzing all five approaches and the specific constraints of this monorepo, our recommendation is a phased hybrid platform that builds on three approaches in sequence.
┌─────────────────────────────────────────────────────────────────────────┐
│ RECOMMENDED PLATFORM — THREE PHASES │
│ │
│ Phase 1 (Foundation): Approach D — Schema-First Contract Registry │
│ Build the source of truth first. │
│ │
│ Phase 2 (Runtime): Approach A — Platform SDK │
│ One execution path for all domains. │
│ │
│ Phase 3 (Intelligence): Approach E — AI Governance Layer │
│ Scale governance without scaling headcount. │
│ │
│ Running alongside: Approach C — Event Bus for multi-vendor future │
│ Prepare for vendor-agnostic dispatch. │
└─────────────────────────────────────────────────────────────────────────┘
Why this order:
Why NOT Approach B alone:
Approach B fixes the symptom (data layer chaos) but not the root cause (no contracts, no governance).
It is useful as a quick win during Phase 1 but not as the final architecture.
┌─────────────────────────────────────────────────────────────────────────────────┐
│ UNIFIED ANALYTICS PLATFORM — SYSTEM LAYERS │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ LAYER 1 — CONTRACT LAYER │ │
│ │ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐│ │
│ │ │ Event Catalog │ │ PV Compiler │ │ Contract Code Generator ││ │
│ │ │ (JSON/YAML) │ │ (AI-assisted) │ │ (TS types + Zod) ││ │
│ │ └────────┬─────────┘ └────────┬─────────┘ └────────────┬─────────────┘│ │
│ │ └──────────────────────┴─────────────────────────┘ │ │
│ │ Contract Registry │ │
│ └────────────────────────────────────┬─────────────────────────────────────┘ │
│ │ typed contracts + Zod schemas │
│ ┌────────────────────────────────────▼─────────────────────────────────────┐ │
│ │ LAYER 2 — SDK LAYER │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐│ │
│ │ │ Analytics Platform SDK ││ │
│ │ │ track() │ page() │ identify() │ impression() ││ │
│ │ │ ───────────────────────────────────────── ││ │
│ │ │ Validate → Enrich → Tenant Resolve → Deduplicate → Dispatch ││ │
│ │ └──────────────────────────────────────────────────────────────────────┘│ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐│ │
│ │ │ Tenant Policy │ │ Data Layer Mgr │ │ Event Bus ││ │
│ │ │ Engine │ │ (merge-only) │ │ (multi-vendor dispatch) ││ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────────────┘│ │
│ └────────────────────────────────────┬─────────────────────────────────────┘ │
│ │ validated, enriched, tenant-resolved │
│ ┌────────────────────────────────────▼─────────────────────────────────────┐ │
│ │ LAYER 3 — DISPATCH LAYER │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Adobe DTM │ │ Google GA4 │ │ Meta Pixel │ │ Amplitude │ │ │
│ │ │ (_satellite)│ │ (future) │ │ (future) │ │ (future) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────▼─────────────────────────────────────┐ │
│ │ LAYER 4 — INTELLIGENCE LAYER │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐│ │
│ │ │ PV → Contract │ │ CI Validator │ │ Drift Detector ││ │
│ │ │ Generator (AI) │ │ (AI + schema) │ │ (E2E + production) ││ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────────────┘│ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ Observability │ │ Incident │ │ │
│ │ │ Dashboard │ │ Root Cause AI │ │ │
│ │ └──────────────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
The single source of truth for all analytics event definitions.
Lives in the monorepo as a versioned package.
Consumed by the PV Compiler, Contract Generator, SDK, and AI Governance Layer.
packages/@analytics/event-catalog/
├── events/
│ ├── search/
│ │ ├── FILTER_CLEARED.json
│ │ ├── PRODUCT_CARD_CLICKED.json
│ │ └── SEARCH_SUBMITTED.json
│ ├── pdp/
│ │ ├── ADD_TO_CART_CLICKED.json
│ │ └── PDP_PAGE_LOADED.json
│ ├── cart/
│ │ ├── CART_VIEWED.json
│ │ └── CHECKOUT_STARTED.json
│ └── checkout/
│ ├── CONTACT_INFO_SUBMITTED.json
│ └── ORDER_CONFIRMED.json
├── tenants/
│ ├── ford-accessories-US-en.json
│ ├── ford-plans-ZA-en.json
│ └── lincoln-accessories-US-en.json
└── schema/
└── event-schema-v1.json
Each event definition:
{
"key": "FILTER_CLEARED",
"version": "1.2.0",
"description": "Fired when user clears all applied filters on PLP",
"track": "ecommerce-onclick",
"domains": ["@nvc/search"],
"required": [
"page.pageName",
"page.siteSection",
"onclick.onclickLinkName",
"onclick.onclick"
],
"schema": {
"page": { "pageName": "string", "siteSection": "string" },
"onclick": { "onclickLinkName": "string", "onclick": "string" },
"user": { "loginStatus": "string?" }
},
"defaults": {
"onclick": {
"onclick": "filter cleared",
"onclickLinkName": "clear filters"
}
},
"tenantOverrides": {
"ford-plans-ZA-en": {
"page": { "siteSection": "ford protect" },
"onclick": { "onclickLinkName": "fz:ford protect:filter cleared" }
}
}
}
Reads the Event Catalog and generates TypeScript types and Zod validators.
Runs as an Nx generator and as a CI step.
No developer ever writes a schema manually.
nx run @analytics/event-catalog:generate
Output:
apps/@nvc/search/analytics/contracts/
├── search-filters.ts ← app-owned event contracts
└── index.ts
apps/@nvc/cart/analytics/contracts/
├── cart-events.ts ← app-owned event contracts
└── index.ts
packages/@analytics/protocol/src/
├── eventEnvelope.ts ← runtime envelope only
├── runtimeGuards.ts
└── index.ts
The only interface all feature teams use for analytics.
No team touches window.digitaldata or _satellite directly.
// Simple API for feature developers
import { analytics } from '@analytics/sdk';
// Page load event
analytics.page('PDP_PAGE_LOADED', {
product: { SKU: '12345', name: 'Mustang Spoiler', price: 299 }
});
// Click event
analytics.track('FILTER_CLEARED', {
label: 'Clear All'
});
// Impression
analytics.impression('PRODUCT_CARD_VIEWED', {
product: { SKU: '12345' },
position: 3
});
The SDK internally:
Resolves which overrides apply for the current brand/commodity/country/language.
Stored as configuration, not code.
// packages/@analytics/tenant-policy/src/policy.json
{
"ford/accessories/US/en": {
"mode": "registry",
"prefix": "fa",
"site": "accessories.ford.com",
"satelliteTracks": {
"pageload": "websdk-pageload",
"onclick": "websdk-onclick"
}
},
"ford/plans/ZA/en": {
"mode": "registry",
"prefix": "fz",
"site": "ford.co.za",
"satelliteTracks": {
"pageload": "global-load",
"onclick": "ecommerce-onclick"
}
},
"lincoln/accessories/US/en": {
"mode": "registry",
"prefix": "la",
"site": "www.lincoln.com/accessories",
"satelliteTracks": {
"pageload": "websdk-pageload",
"onclick": "websdk-onclick"
}
},
"*": {
"mode": "legacy"
}
}
Replaces all direct window.digitaldata writes.
Enforces merge-only policy.
Exposes typed push API.
Maintains event history for debugging.
// packages/@analytics/data-layer/src/dataLayerManager.ts
class DataLayerManager {
private state: Record<string, unknown> = {};
private history: AnalyticsEvent[] = [];
push(event: AnalyticsEnvelope): void {
this.state = deepMerge(this.state, event.data); // ALWAYS merge, NEVER replace
this.history.push(event);
this.notify(event);
}
snapshot(): Record<string, unknown> {
return structuredClone(this.state);
}
reset(scope: 'onclick' | 'event'): void {
// Scoped reset only — cannot wipe page or user fields
delete this.state[scope];
}
}
Reads business PV documents (Excel, PDF, JSON) and generates Event Catalog entries.
Not a chatbot. A CI/CD pipeline tool.
Input: pv-checkout-v3.xlsx (Excel from analytics team)
Output: events/checkout/CONTACT_INFO_SUBMITTED.json
Process:
1. LLM reads PV row structure
2. Maps PV columns to event schema template
3. Identifies required fields, value mappings, satellite tracks
4. Generates JSON event definition
5. Human review required before catalog merge (AI + human gate)
Runs on every PR that touches analytics-related files.
Validates that component implementation matches catalog contract.
Explains mismatches in plain English using AI.
# .github/workflows/analytics-validation.yml
- name: Analytics Contract Validation
run: nx run @analytics/validator:check --pr=${{ github.event.pull_request.number }}
# Output example:
# ❌ FILTER_CLEARED event validation failed
#
# Component: apps/@nvc/search/src/filterInteraction/filterClear.tsx
# Contract: packages/@analytics/event-catalog/events/search/FILTER_CLEARED.json
#
# Missing required field: page.siteSection
# Found: { onclick: { onclickLinkName: "clear filters" } }
# Expected: { page: { siteSection: string }, onclick: { ... } }
#
# Fix: Add the ShopAnalyticsProvider context or pass page.siteSection explicitly.
Real-time visibility into analytics quality across all tenants.
Metrics:
This shows how a business PV document becomes a live analytics event in production — with minimal human error.
Shows how one event definition becomes market-specific without code branching.
┌─────────────────────────────────────────────────────────────────────────┐
│ ANALYTICS PLATFORM TEAM — STRUCTURE │
├─────────────────┬───────────────────────────────────────────────────────┤
│ Role │ Responsibility │
├─────────────────┼───────────────────────────────────────────────────────┤
│ Platform Lead │ Architecture decisions, cross-team coordination, │
│ │ migration oversight, vendor relationships │
├─────────────────┼───────────────────────────────────────────────────────┤
│ Contract │ Event Catalog maintenance, schema review, │
│ Engineer │ Contract Generator, PV Compiler tooling │
├─────────────────┼───────────────────────────────────────────────────────┤
│ SDK Engineer │ Platform SDK, Data Layer Manager, Event Bus, │
│ │ Tenant Policy Engine │
├─────────────────┼───────────────────────────────────────────────────────┤
│ AI Engineer │ PV Compiler AI, CI Validator AI, Drift Detector, │
│ │ Incident Root Cause AI │
├─────────────────┼───────────────────────────────────────────────────────┤
│ Quality │ E2E analytics validation, Zod schema coverage, │
│ Engineer │ Observability dashboards, schema conformance reports │
├─────────────────┼───────────────────────────────────────────────────────┤
│ Domain Liaisons │ One embedded rep per domain (NVC, NaBuy, FBC, IMG) │
│ (part-time) │ Responsible for domain migration and adoption │
└─────────────────┴───────────────────────────────────────────────────────┘
Governance flow:
| Component | Build Internally | Buy / Use Existing |
|---|---|---|
| Event Catalog | ✅ Build — monorepo-native, Git-versioned, TypeScript | Alternative: Segment Protocols, Amplitude Govern |
| Contract Generator | ✅ Build — Nx generator, 2-week effort | N/A — none exist for this stack |
| Platform SDK | ✅ Build — TypeScript, monorepo-aware, multi-tenant | Alternative: Segment Analytics.js (vendor lock-in) |
| Data Layer Manager | ✅ Build — simple, 1-week effort | Alternative: W3C CEDDL implementation |
| Tenant Policy Engine | ✅ Build — JSON config, easy | N/A |
| AI PV Compiler | 🔄 Hybrid — build orchestration, use LLM API | OpenAI API / Claude API as backend |
| CI Validator | ✅ Build — Nx task + schema check | GitHub Actions marketplace options exist |
| Event Bus | ✅ Build — lightweight, 1-week | Alternative: mitt, EventEmitter, custom |
| Observability Dashboard | 🔄 Hybrid — use Grafana, build custom schemas | Grafana + custom data pipeline |
| AI Root Cause | 🔄 Hybrid — build agent, use LLM API | N/A |
Estimated total build effort:
| Phase | Team Size | Duration | Deliverable |
|---|---|---|---|
| Phase 1 | 2 engineers | 8 weeks | Event Catalog + Contract Generator |
| Phase 2 | 3 engineers | 10 weeks | SDK + Data Layer + Tenant Engine |
| Phase 3 | 3 engineers + 1 AI | 12 weeks | CI Validator + AI PV Compiler + Migration |
| Phase 4 | 2 engineers + 1 AI | 8 weeks | Observability + AI Root Cause |
| Phase 5 | All teams | 6 weeks | Legacy decommission |
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Teams resist SDK adoption | Medium | High | Phase rollout, SDK wraps legacy initially, domain liaisons |
| AI PV Compiler generates wrong schemas | Medium | High | Human review gate before catalog merge, validation layer |
| Migration breaks live analytics | Low | Very High | Dual-run mode, parity checks, tenant-by-tenant rollout |
| SDK performance overhead | Low | Medium | SDK is synchronous, adds < 1ms, benchmarked |
| Event Catalog becomes stale | Medium | High | CI enforces no new analytics without catalog entry |
| Governance bottleneck at platform team | Medium | Medium | Async approval workflow, clear SLA on contract reviews |
| Multi-tenant edge cases missed | Medium | Medium | Contract test matrix per tenant, automated parity checks |
If we engage an external vendor for any part of this platform, these are the non-negotiable requirements:
_satellite infrastructureDocument maintained by: Ford Analytics Platform Architecture Team
Classification: Internal Architecture — Senior Distribution
Next document: LLD — Low Level Design (request when ready)
Last updated: 2026-06-12