After studying this material, you should be able to:
Imagine you are a developer querying data. You receive this instruction:
"Use widget_metrics_v2 for data after 2025. Use widget_metrics_v1 before 2025. Also, the column was renamed."
This creates several serious problems:
| Problem | Consequence |
|---|---|
| Queries are tightly coupled to table structure | Changing a table breaks many queries |
| Multiple versions of truth exist | Developers must manually manage which table to use |
| Schema changes require widespread updates | Slow, error-prone, expensive |
| New reports take weeks to build | Business moves slower than it should |
In software engineering, an abstraction hides complexity behind a simple interface.
Without abstraction: Consumer must know HOW data is stored
With abstraction: Consumer only specifies WHAT data they want
Key Insight: The DAL applies this same principle to data access.
A Data Abstraction Layer (DAL) is an RPC (Remote Procedure Call) service that sits between data consumers and data producers.
┌──────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ DATA CONSUMER │ ──────► │ DAL │ ──────► │ DATA PRODUCER │
│ (e.g., front end)│ ◄────── │ (the service) │ ◄────── │ (databases/tables│
└──────────────────┘ └─────────────────┘ └──────────────────┘
Asks WHAT it wants Figures out HOW Stores actual data
The DAL exposes one primary endpoint: FetchData
A request contains:
These are two fundamental concepts you must understand:
| Concept | Definition | Example |
|---|---|---|
| Dimension | A classification used for grouping | campaign ID, date, region |
| Metric | A measurement that can be aggregated | clicks, impressions, spend |
Request: "Give me clicks and impressions, broken down by campaign, for the last 7 days"
Table: ads.advertiser_metrics
Schema: dimensions: [ad_campaign_uuid]
metrics: [clicks, impressions]
Time Range: last 7 days
Filter: account_uuid = "ABC123"
Key Design Principle:
The consumer specifies WHAT they want. The DAL determines HOW to get it.
If you remove a dimension from your request, the DAL automatically aggregates the data to the appropriate level.
With ad_campaign_uuid → Data broken down per campaign
Without ad_campaign_uuid → Data rolled up to account level (automatically)
The client does nothing extra. The DAL handles it.
This distinction is central to understanding the DAL.
A logical table defines the shape of data without describing where or how to get it.
Think of it as a contract or interface:
ads.advertiser_metrics (LOGICAL TABLE)
├── Dimensions: account_uuid, ad_campaign_uuid, day_of
└── Metrics:
├── clicks → source: [realtime_metrics, daily_metrics]
├── impressions → source: [realtime_metrics, daily_metrics]
└── avg_score → source: [daily_metrics only]
A physical table is the actual database table where data lives. It has:
Consumer queries logical table → Always the same interface
Physical tables change → Consumer is unaffected
New table added → DAL routes automatically
Old table deprecated → DAL stops using it silently
Table resolution is the process of generating a query plan — deciding exactly which physical tables to query, for which time ranges, and for which columns.
"Can this table even answer the question?"
Request asks for: clicks, impressions, ad_campaign_uuid
Table A has: clicks, impressions, ad_campaign_uuid ✅ Keep
Table B has: clicks only, no ad_campaign_uuid ❌ Discard
Table C has: avg_score only ❌ Discard
"Does this table cover the time range I need?"
Each physical table defines an availability window:
Request: last 7 days
Realtime table: covers last 0-3 days ✅ Partially covers
Daily table: covers last 3-90 days ✅ Partially covers
Archive table: covers 1-5 years ago ❌ Discard (no overlap)
"When multiple tables overlap, which one is better?"
Days 1-3: Use Realtime table (more granular, fresher)
Days 4-7: Use Daily table (only option for older data)
The result is a concrete execution plan:
Query 1: realtime_metrics → metrics: [clicks, impressions] → days 1-3
Query 2: hourly_metrics → metrics: [clicks] → days 1-3
Query 3: daily_metrics → metrics: [clicks, impressions] → days 4-7
For each table in the execution plan, the DAL generates a database-specific query that:
The DAL supports multiple database technologies simultaneously:
Single consumer request might trigger:
├── Query to OLAP database (for recent metrics)
├── Query to Apache Hive (for historical data)
└── Query to Docstore (for reference data)
The consumer never needs to know which database is being used.
All queries execute in parallel to minimize latency. The DAL waits for all responses before proceeding to result assembly.
Once all query responses return, the DAL assembles them into a single coherent result.
| Operation | When Used | What It Does |
|---|---|---|
| Concatenation | Temporally adjacent datasets | Stitches time ranges together |
| Joins | Complementary datasets | Combines datasets that each cover different metrics |
| Rollups | When aggregation wasn't done at query time | Aggregates data to the requested granularity |
| Scalar Transformations | Special columns | Handles virtual columns, normalizations, reference joins |
Query 1 result: [days 1-3, clicks + impressions]
Query 2 result: [days 4-7, clicks + impressions]
Assembly step: CONCATENATE
Final result: [days 1-7, clicks + impressions] ✅ Sent to consumer
| Scenario | Before DAL | After DAL |
|---|---|---|
| Build a new report | Several weeks to months | Under 2 days |
| Change a table schema | Update all dependent queries | Update DAL config only |
| Add a new database | Rewrite consumer code | Add a connector to DAL |
| Dynamic user-specified reports | Extremely complex | Straightforward |
A pivot table or report builder lets users choose their own dimensions and metrics. With traditional approaches, this is nearly impossible because queries are hardcoded.
With the DAL:
User selects: [campaign, region] as dimensions + [clicks, spend] as metrics
DAL receives: a FetchData request with that schema
DAL handles: all routing, querying, and assembly automatically
CONSUMER
│
"I want clicks by campaign
for the last 7 days"
│
▼
┌────────────────┐
│ LOGICAL TABLE │ ← Stable interface (never changes)
│ advertiser_ │
│ metrics │
└────────┬───────┘
│
TABLE RESOLUTION
(3 phases: eligibility,
availability, continuity)
│
┌────────┴────────┐
▼ ▼
realtime_table daily_table
(days 1-3) (days 4-7)
│ │
PARALLEL QUERIES │
└────────┬────────┘
│
RESULT ASSEMBLY
(concatenate, join,
rollup, transform)
│
▼
UNIFIED RESPONSE
sent to consumer
Test yourself with these questions:
Mastering these concepts gives you a strong foundation for understanding modern data infrastructure design, abstraction patterns in distributed systems, and how large-scale platforms like Uber manage data complexity at scale.