ScaledByDesign/Insights
ServicesPricingAboutContact
Book a Call
Scaled By Design

Fractional CTO + execution partner for revenue-critical systems.

Company

  • About
  • Services
  • Contact

Resources

  • Insights
  • Pricing
  • FAQ

Legal

  • Privacy Policy
  • Terms of Service

© 2026 ScaledByDesign. All rights reserved.

contact@scaledbydesign.com

On This Page

The Trust ProblemWhat a Data Layer Actually IsThe ArchitectureLayer 1: Event SchemaLayer 2: Event EmissionLayer 3: Tool AdaptersThe Validation LayerThe Data Quality DashboardImplementation RoadmapWeek 1: Define the SchemaWeek 2: Build the Data LayerWeek 3: Connect ToolsWeek 4: Monitor and MaintainThe Result
  1. Insights
  2. Split Testing & Tracking
  3. The Data Layer Architecture That Makes Every Test Trustworthy

The Data Layer Architecture That Makes Every Test Trustworthy

January 27, 2026·ScaledByDesign·
data-layeranalyticsarchitecturetracking

The Trust Problem

Your A/B testing tool says variant B increased revenue by 12%. Google Analytics says revenue was flat. Facebook claims it drove 40% of the conversions. Your database shows a number that doesn't match any of them.

Which one do you trust? If the answer isn't immediately obvious, your data layer is broken — and every decision you make from this data is a coin flip.

What a Data Layer Actually Is

A data layer is a standardized, structured object that serves as the single source of truth for all tracking, analytics, and experimentation tools on your site.

// The data layer: one object, one truth
window.dataLayer = {
  page: {
    type: "product",
    category: "shoes",
    name: "Air Max 90",
  },
  user: {
    id: "usr_abc123",
    type: "returning",
    segment: "high_value",
    isLoggedIn: true,
  },
  product: {
    id: "sku_789",
    name: "Air Max 90",
    price: 129.99,
    currency: "USD",
    category: "shoes/running",
    variant: "black/white",
    inStock: true,
  },
  cart: {
    items: 3,
    total: 287.97,
    currency: "USD",
  },
  experiment: {
    id: "checkout-redesign-q1",
    variant: "streamlined",
  },
};

Every tool — GA4, Facebook, your A/B testing platform, your CDP — reads from this same object. No tool gets special data. No tool collects its own version of the truth.

The Architecture

                    ┌─────────────────┐
                    │   Data Layer    │
                    │  (Single Truth) │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
        │  GA4 Tag  │ │  FB CAPI  │ │  A/B Test  │
        │           │ │  (server) │ │  Platform  │
        └───────────┘ └───────────┘ └───────────┘

Layer 1: Event Schema

Define every event your business cares about:

// Event schema — the contract between your site and all tools
type TrackingEvents = {
  "page.viewed": {
    pageType: string;
    pageTitle: string;
    url: string;
  };
  "product.viewed": {
    productId: string;
    productName: string;
    price: number;
    currency: string;
    category: string;
  };
  "product.added_to_cart": {
    productId: string;
    productName: string;
    price: number;
    quantity: number;
    cartTotal: number;
  };
  "checkout.started": {
    cartTotal: number;
    itemCount: number;
    currency: string;
  };
  "order.completed": {
    orderId: string;
    revenue: number;
    tax: number;
    shipping: number;
    currency: string;
    items: OrderItem[];
    isFirstOrder: boolean;
    paymentMethod: string;
  };
};

The rule: If an event isn't in the schema, it doesn't get tracked. No ad-hoc tracking, no "just add this pixel real quick."

Layer 2: Event Emission

// One function to emit events — all tools consume from here
function trackEvent<T extends keyof TrackingEvents>(
  eventName: T,
  properties: TrackingEvents[T]
) {
  const event = {
    name: eventName,
    properties,
    timestamp: Date.now(),
    sessionId: getSessionId(),
    userId: getUserId(),
    deviceId: getDeviceId(),
  };
 
  // Validate against schema
  if (!validateEvent(eventName, properties)) {
    console.error(`Invalid event: ${eventName}`, properties);
    reportToMonitoring("invalid_tracking_event", { eventName });
    return;
  }
 
  // Push to data layer
  window.dataLayer.push({ event: eventName, ...properties });
 
  // Send server-side (for platforms that support it)
  sendServerSide(event);
}

Layer 3: Tool Adapters

Each tool gets an adapter that translates from your schema to its format:

// GA4 adapter
function ga4Adapter(event: TrackingEvent) {
  const mapping: Record<string, string> = {
    "product.viewed": "view_item",
    "product.added_to_cart": "add_to_cart",
    "checkout.started": "begin_checkout",
    "order.completed": "purchase",
  };
 
  const ga4Event = mapping[event.name];
  if (!ga4Event) return;
 
  gtag("event", ga4Event, {
    currency: event.properties.currency,
    value: event.properties.revenue || event.properties.price,
    items: formatItemsForGA4(event.properties),
  });
}
 
// Facebook adapter
function facebookAdapter(event: TrackingEvent) {
  const mapping: Record<string, string> = {
    "product.viewed": "ViewContent",
    "product.added_to_cart": "AddToCart",
    "checkout.started": "InitiateCheckout",
    "order.completed": "Purchase",
  };
 
  const fbEvent = mapping[event.name];
  if (!fbEvent) return;
 
  // Server-side via CAPI (not client-side pixel)
  sendToFacebookCAPI(fbEvent, event.properties);
}

The Validation Layer

This is what separates trustworthy data from garbage:

// Real-time event validation
function validateEvent(
  eventName: string,
  properties: Record<string, unknown>
): ValidationResult {
  const checks: ValidationCheck[] = [
    // Required fields present
    checkRequiredFields(eventName, properties),
 
    // Types are correct
    checkFieldTypes(eventName, properties),
 
    // Values are reasonable
    checkValueRanges(eventName, properties),
 
    // No duplicate events (within 1 second)
    checkDuplication(eventName, properties),
  ];
 
  return {
    valid: checks.every(c => c.passed),
    errors: checks.filter(c => !c.passed).map(c => c.error),
  };
}
 
// Example: catch common tracking bugs
function checkValueRanges(
  eventName: string,
  properties: Record<string, unknown>
): ValidationCheck {
  if (eventName === "order.completed") {
    const revenue = properties.revenue as number;
 
    // Revenue should be positive
    if (revenue <= 0) {
      return { passed: false, error: "Revenue is zero or negative" };
    }
 
    // Revenue shouldn't be absurdly high (likely a bug)
    if (revenue > 50000) {
      return { passed: false, error: `Revenue suspiciously high: ${revenue}` };
    }
 
    // Order should have items
    const items = properties.items as unknown[];
    if (!items || items.length === 0) {
      return { passed: false, error: "Order has no items" };
    }
  }
 
  return { passed: true };
}

The Data Quality Dashboard

Monitor your data layer health daily:

Data Layer Health: Feb 8, 2026

Events Today: 142,847
├── Valid: 141,203 (98.8%) ✅
├── Invalid: 1,644 (1.2%)
│   ├── Missing required fields: 892
│   ├── Type mismatches: 412
│   ├── Duplicate events: 340
│   └── Out-of-range values: 0
└── Dropped (validation failed): 0

Platform Parity:
├── GA4 events received: 141,203 ✅ (matches)
├── Facebook CAPI events: 28,400 (purchase + cart events only) ✅
├── A/B test exposures: 34,200 ✅
└── Internal database: 141,203 ✅ (matches)

Revenue Reconciliation:
├── Data layer total: $89,420
├── Stripe settlements: $89,180
├── Variance: 0.27% ✅ (< 1% threshold)
└── GA4 reported: $87,200 (2.5% under — known sampling)

Implementation Roadmap

Week 1: Define the Schema

  • List every event your business needs
  • Define required and optional properties for each
  • Get sign-off from marketing, product, and engineering

Week 2: Build the Data Layer

  • Implement the event emission function
  • Add validation
  • Set up server-side forwarding

Week 3: Connect Tools

  • Build adapters for each platform (GA4, Facebook, etc.)
  • Verify events are received correctly
  • Compare against existing tracking

Week 4: Monitor and Maintain

  • Build the data quality dashboard
  • Set up alerts for validation failures
  • Run daily reconciliation against payment processor

The Result

When your data layer is solid:

  • Every tool shows the same numbers (within sampling variance)
  • A/B test results are trustworthy because exposure and conversion data come from the same source
  • New tools can be added in hours, not weeks
  • Data quality issues are caught in real-time, not discovered months later

The data layer isn't glamorous work. But it's the foundation that makes every experiment, every dashboard, and every business decision trustworthy. Without it, you're making million-dollar decisions on data you can't verify.

Build the foundation. Then trust the numbers.

Previous
When to Hire a Fractional CTO vs a Full-Time CTO
Next
Scale Postgres Before Reaching for NoSQL
Insights
A/B Testing Is Lying to You — Statistical Significance Isn't EnoughServer-Side Split Testing: Why Client-Side Tools Are Costing You RevenueThe Tracking Stack That Survives iOS, Ad Blockers, and Cookie DeathHow to Run Pricing Experiments Without Destroying TrustYour Conversion Rate Is a Vanity Metric — Here's What to Track InsteadBuilding a Feature Flag System That Doesn't Become Technical DebtThe Data Layer Architecture That Makes Every Test Trustworthy

Ready to Ship?

Let's talk about your engineering challenges and how we can help.

Book a Call