Chapter 18: Data Warehousing & Data Lakes

Modern organizations collect data from hundreds of sources—transactional databases, event streams, APIs, logs, third-party vendors. The challenge isn't just storing this data, but transforming it into a unified, queryable asset that powers analytics, ML, and business intelligence.

This chapter explores three fundamental architectures for analytical data: Data Warehouses (structured, schema-first, optimized for BI), Data Lakes (schema-on-read, raw data preservation, flexibility), and Lakehouses (combining the best of both). We'll cover ETL vs ELT, dimensional modeling, modern formats, orchestration tools, and how to choose the right approach for your scale and use case.

Why This Matters: In system design interviews, you'll be asked to design analytics platforms, recommendation systems, or ML pipelines. Understanding the tradeoffs between warehouses, lakes, and lakehouses—and knowing when to use ETL vs ELT—demonstrates architectural maturity.

1. Data Warehouse vs Data Lake vs Lakehouse

1.1 Definitions

Data Warehouse

A data warehouse is a centralized repository optimized for analytical queries. Data is extracted from source systems, transformed into a consistent schema (often dimensional models like star or snowflake schemas), and loaded for BI tools to query.

Data Lake

A data lake stores raw data in its native format—structured, semi-structured, or unstructured. Schema is applied at query time (schema-on-read), enabling maximum flexibility.

Lakehouse

A lakehouse combines data lake flexibility with data warehouse reliability. It uses open table formats (Delta Lake, Iceberg, Hudi) to bring ACID transactions, schema enforcement, and time travel to data lakes.

1.2 Comparison Table

Dimension Data Warehouse Data Lake Lakehouse
Data Types Structured only All (structured, semi-, unstructured) All types
Schema Schema-on-write Schema-on-read Enforced with evolution
ACID Full ACID None (eventual consistency) Full ACID via table formats
Storage Cost High (proprietary formats) Low (object storage) Low (object storage)
Query Performance Very fast (optimized indexes) Slow without optimization Fast (with partitioning + indexes)
Time Travel Limited (point-in-time snapshots) None Native (version history)
ML Support Poor (need to export data) Excellent (direct file access) Excellent (unified access)
BI Support Excellent (SQL-first) Poor (require complex queries) Excellent (SQL + Spark)
Best For Dashboards, reports, KPIs Data science, archival, raw data Unified analytics (BI + ML)

1.3 Evolution Diagram

graph LR
    A[2000s: Data Warehouses] -->|Too rigid for ML| B[2010s: Data Lakes]
    B -->|Quality issues, no ACID| C[2020s: Lakehouses]

    A -->|Schema-on-write| D[Snowflake, Redshift]
    B -->|Schema-on-read| E[S3 + Athena]
    C -->|Best of both| F[Delta Lake, Iceberg]

    style C fill:#2d5016
      
Interview Tip: When asked about data architecture, clarify requirements first. If the interviewer mentions "real-time dashboards + ML model training," suggest a lakehouse. If they emphasize "stable BI reports for executives," a warehouse suffices. If they say "store everything, figure out schema later," a data lake fits.

2. ETL vs ELT

2.1 Extract-Transform-Load (ETL)

ETL is the traditional approach: data is extracted from sources, transformed on a staging server or ETL tool, then loaded into the target warehouse.

graph LR
    A[Source DB] -->|Extract| B[Staging Area]
    B -->|Transform| C[ETL Server]
    C -->|Load| D[Data Warehouse]

    style C fill:#4a235a
      

When to Use ETL

Pros & Cons

Pros Cons
Less warehouse compute needed Separate ETL infrastructure to maintain
Data cleaned before landing No raw data for re-processing
Works with limited warehouse power Slow to iterate (rerun ETL for schema changes)

2.2 Extract-Load-Transform (ELT)

ELT is the modern approach: raw data is loaded directly into the warehouse/lake, then transformed using the warehouse's compute power (SQL, dbt).

graph LR
    A[Source DB] -->|Extract| B[Ingestion Tool]
    B -->|Load Raw| C[Data Warehouse]
    C -->|Transform| D[Analytics Layer]

    style C fill:#1a5490
      

When to Use ELT

Pros & Cons

Pros Cons
Preserve raw data for flexibility Higher warehouse compute costs
Leverage warehouse's scalability PII lands in warehouse (governance risk)
Faster to change transformations Requires powerful warehouse
Simpler infrastructure (no ETL server) SQL-first (less flexibility than Python/Java ETL)

2.3 ETL vs ELT Pipeline Comparison

flowchart TB
    subgraph ETL_Pipeline[ETL Pipeline - Traditional]
        A1[MySQL] --> B1[Extract]
        A2[PostgreSQL] --> B1
        A3[API] --> B1
        B1 --> C1[Transform on ETL Server]
        C1 --> D1[Load to Warehouse]
        D1 --> E1[Query Layer]
    end

    subgraph ELT_Pipeline[ELT Pipeline - Modern]
        A4[MySQL] --> B2[Fivetran]
        A5[PostgreSQL] --> B2
        A6[API] --> B2
        B2 --> C2[Load Raw to Snowflake]
        C2 --> D2[dbt Transform in Warehouse]
        D2 --> E2[Query Layer]
    end

    style ETL_Pipeline fill:#4a235a,stroke:#fff
    style ELT_Pipeline fill:#1a5490,stroke:#fff
      
Modern Trend: ELT has become the default for cloud-native data stacks. Tools like dbt (data build tool) make SQL-based transformations version-controlled, tested, and modular—treating data pipelines like software engineering.

3. Data Warehouse Architecture

3.1 Kimball (Dimensional Modeling)

The Kimball approach (Ralph Kimball, 1996) organizes data into fact tables and dimension tables, optimized for BI queries. It's denormalized for read performance.

Key Concepts

erDiagram
    FACT_SALES {
        int sale_id PK
        int customer_id FK
        int product_id FK
        int date_id FK
        decimal amount
        int quantity
    }

    DIM_CUSTOMER {
        int customer_id PK
        string name
        string email
        string segment
    }

    DIM_PRODUCT {
        int product_id PK
        string name
        string category
        decimal price
    }

    DIM_DATE {
        int date_id PK
        date full_date
        int year
        int quarter
        int month
    }

    FACT_SALES }o--|| DIM_CUSTOMER : "customer_id"
    FACT_SALES }o--|| DIM_PRODUCT : "product_id"
    FACT_SALES }o--|| DIM_DATE : "date_id"
      

Pros & Cons

Pros Cons
Fast queries (denormalized, fewer joins) Data duplication (denormalization)
Business-user-friendly (intuitive model) Rigid schema (hard to add new sources)
Optimized for BI dashboards Not ideal for ad-hoc exploration

3.2 Inmon (Normalized/Enterprise Data Warehouse)

The Inmon approach (Bill Inmon, 1990s) advocates a normalized, 3NF (Third Normal Form) data warehouse. Data is stored in a clean, consistent format, then transformed into data marts for specific business units.

Key Concepts

flowchart TB
    A[Source Systems] --> B[Staging Area]
    B --> C[Enterprise Data Warehouse - Normalized]
    C --> D1[Sales Data Mart]
    C --> D2[Finance Data Mart]
    C --> D3[Marketing Data Mart]
    D1 --> E[BI Tools]
    D2 --> E
    D3 --> E

    style C fill:#4a235a
      

Pros & Cons

Pros Cons
No data duplication (normalized) Slower queries (many joins)
Easier to integrate new sources Complex to build and maintain
Single source of truth Data marts add latency

3.3 Kimball vs Inmon Comparison

Dimension Kimball (Dimensional) Inmon (Normalized)
Approach Bottom-up (start with business needs) Top-down (enterprise-wide model first)
Schema Denormalized (star/snowflake) Normalized (3NF)
Query Performance Fast (fewer joins) Slower (many joins)
Storage Higher (duplication) Lower (normalized)
Time to Value Fast (build marts incrementally) Slow (model entire enterprise first)
Flexibility Less flexible (schema changes hard) More flexible (easier to add sources)
Best For BI, dashboards, known queries Enterprise-wide consistency, auditing
Interview Insight: In practice, most modern warehouses use a hybrid: Inmon-style normalized staging layers (for flexibility) + Kimball-style dimensional marts (for BI performance). This is often called the "Corporate Information Factory" (CIF).

3.4 Medallion Architecture (Bronze/Silver/Gold)

The medallion architecture (popularized by Databricks) organizes data lakes and lakehouses into three layers of increasing quality:

flowchart LR
    A[Data Sources] --> B[Bronze Layer - Raw]
    B --> C[Silver Layer - Cleansed]
    C --> D[Gold Layer - Curated]

    B --> E[Schema-on-read, All data]
    C --> F[Schema-enforced, Validated]
    D --> G[Star schema, Aggregated]

    D --> H[BI Tools]
    D --> I[ML Models]

    style B fill:#8b4513
    style C fill:#c0c0c0
    style D fill:#ffd700
      

When to Use Medallion


4. Star Schema Deep Dive

4.1 Fact and Dimension Tables

Fact Table

A fact table stores quantitative measurements (metrics) at a specific grain. Each row represents a business event or transaction.

FACT_ORDERS:
order_id (PK)
customer_id (FK)
product_id (FK)
date_id (FK)
quantity (measure)
unit_price (measure)
total_amount (measure)

Dimension Table

A dimension table provides descriptive context. Dimensions are typically denormalized (contain hierarchies flattened into one table).

DIM_PRODUCT:
product_id (PK)
product_name
category
subcategory
brand
supplier

4.2 Grain

Grain is the most important decision in fact table design. It defines what each row represents. Common grains:

Golden Rule: All facts in a fact table must be at the same grain. Mixing grains (e.g., some rows are orders, some are order lines) leads to double-counting.

4.3 Conformed Dimensions

A conformed dimension is shared across multiple fact tables. For example, DIM_CUSTOMER is used by FACT_SALES, FACT_RETURNS, and FACT_SUPPORT_TICKETS.

graph TD
    A[DIM_CUSTOMER] --> B[FACT_SALES]
    A --> C[FACT_RETURNS]
    A --> D[FACT_SUPPORT_TICKETS]

    style A fill:#2d5016
      

Why Conformed Dimensions Matter

4.4 Role-Playing Dimensions

A role-playing dimension is a single physical dimension referenced multiple times by a fact table in different roles.

Example: FACT_ORDERS has order_date_id, ship_date_id, and delivery_date_id, all pointing to DIM_DATE. Create views to clarify the role:

-- Views for role-playing dimensions
CREATE VIEW dim_order_date AS SELECT * FROM dim_date;
CREATE VIEW dim_ship_date AS SELECT * FROM dim_date;
CREATE VIEW dim_delivery_date AS SELECT * FROM dim_date;

4.5 Star Schema vs Snowflake Schema

Dimension Star Schema Snowflake Schema
Normalization Denormalized dimensions Normalized dimensions (hierarchies split)
Query Performance Faster (fewer joins) Slower (more joins)
Storage Higher (duplication) Lower (normalized)
Ease of Use Simpler for BI tools More complex queries
When to Use Default choice for BI Large dimensions with deep hierarchies
graph LR
    subgraph Star[Star Schema - Denormalized]
        FS[FACT_SALES] --> DP[DIM_PRODUCT - product, category, brand]
    end

    subgraph Snowflake[Snowflake Schema - Normalized]
        FSN[FACT_SALES] --> DPN[DIM_PRODUCT - product]
        DPN --> DC[DIM_CATEGORY - category]
        DC --> DB[DIM_BRAND - brand]
    end

    style Star fill:#1a5490
    style Snowflake fill:#4a235a
      

5. Data Lake Architecture

5.1 Core Principles

A data lake stores raw data in its native format on cheap object storage (AWS S3, Azure Data Lake Storage, Google Cloud Storage). Schema is applied at query time.

Key Characteristics

5.2 Lake Zones (Raw/Cleansed/Curated)

Organize data lakes into zones to separate raw data from processed data:

flowchart LR
    A[Data Sources] --> B[s3://lake/raw/]
    B --> C[Spark Job: Clean, Validate]
    C --> D[s3://lake/cleansed/]
    D --> E[Spark Job: Aggregate, Join]
    E --> F[s3://lake/curated/]

    F --> G[Athena Queries]
    F --> H[ML Models]

    style B fill:#8b4513
    style D fill:#4682b4
    style F fill:#2d5016
      

5.3 File Formats

Format Type Compression Schema Evolution Use Case
Parquet Columnar Excellent (Snappy, Gzip) Limited Analytics, OLAP queries
ORC Columnar Excellent (Zlib, Snappy) Limited Hive/Hadoop ecosystems
Avro Row-based Good (Snappy, Deflate) Excellent Streaming, schema evolution
Delta Lake Parquet + logs Excellent Full support Lakehouse (ACID on lakes)
JSON Row-based Poor Flexible Raw ingestion, APIs
CSV Row-based Poor None Legacy exports
Best Practice: Store raw data as JSON/CSV (easy ingestion), convert to Parquet in the cleansed zone (10x faster queries + 5x less storage).

5.4 Partitioning Strategies

Partitioning divides data into subdirectories, allowing query engines to skip irrelevant files (predicate pushdown).

Common Partitioning Schemes

-- By date (most common)
s3://lake/orders/year=2025/month=05/day=12/part-0000.parquet

-- By region and date
s3://lake/orders/region=us-east/year=2025/month=05/part-0000.parquet

-- By entity
s3://lake/events/event_type=page_view/date=2025-05-12/part-0000.parquet

Partitioning Best Practices

5.5 Data Lake Challenges

Challenge Impact Solution
Data Swamps Unorganized, undocumented data Data catalog, governance, zones
No ACID Inconsistent reads during writes Use lakehouse formats (Delta, Iceberg)
Slow Queries Full table scans without indexes Partitioning, columnar formats, caching
Schema Drift Breaking changes to file schemas Schema registries, validation pipelines
Small Files Problem Listing overhead, slow queries Compaction jobs, batch writes

6. Lakehouse Architecture

6.1 The Lakehouse Vision

A lakehouse combines the flexibility of data lakes with the performance and reliability of data warehouses. It brings ACID transactions, schema enforcement, and time travel to object storage.

graph TB
    A[Data Sources] --> B[Object Storage - S3/ADLS/GCS]
    B --> C[Metadata Layer - Delta Lake/Iceberg]
    C --> D1[BI Tools - Tableau, Looker]
    C --> D2[ML Tools - Spark, Databricks]
    C --> D3[SQL Engines - Athena, Presto]

    style C fill:#ffd700
      

6.2 Delta Lake

Delta Lake (by Databricks, open-source) adds a transaction log on top of Parquet files, enabling ACID guarantees.

Key Features

-- Create Delta table
CREATE TABLE orders USING DELTA
LOCATION 's3://lake/orders';

-- Time travel
SELECT * FROM orders VERSION AS OF 5;
SELECT * FROM orders TIMESTAMP AS OF '2025-05-10';

-- Merge (upsert)
MERGE INTO orders AS target
USING updates AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;

-- Vacuum old versions (delete files older than 7 days)
VACUUM orders RETAIN 168 HOURS;

6.3 Apache Iceberg

Apache Iceberg (by Netflix, donated to Apache) is an open table format designed for large-scale analytics. It's engine-agnostic (works with Spark, Trino, Flink).

Key Features

-- Create Iceberg table
CREATE TABLE orders (
  order_id BIGINT,
  customer_id BIGINT,
  order_date DATE
) USING iceberg
PARTITIONED BY (days(order_date));

-- Time travel
SELECT * FROM orders VERSION AS OF 12345678;

-- Partition evolution (change partitioning without rewrite)
ALTER TABLE orders SET PARTITION SPEC (months(order_date));

6.4 Apache Hudi

Apache Hudi (Hadoop Upserts Deletes Incrementals, by Uber) optimizes for streaming data and incremental processing.

Key Features

6.5 Delta Lake vs Iceberg vs Hudi

Feature Delta Lake Iceberg Hudi
Maturity Mature (2019) Mature (2018) Mature (2017)
ACID Yes (optimistic) Yes (optimistic) Yes
Time Travel Yes Yes Yes
Schema Evolution Good Excellent Good
Partition Evolution No (rewrite needed) Yes (hidden partitioning) Limited
Incremental Reads Via change data feed Snapshot diffs Built-in (core feature)
Engine Support Spark, Flink, Presto Spark, Trino, Flink, Hive Spark, Flink, Hive
Best For Databricks ecosystem Multi-engine, large orgs Streaming upserts, CDC
Interview Tip: If the interviewer mentions "ACID on data lakes," ask clarifying questions. Delta Lake is easiest if they use Databricks. Iceberg is best for multi-engine support (Trino + Spark + Flink). Hudi excels at streaming upserts (CDC pipelines).

6.6 Lakehouse Architecture Diagram

flowchart TB
    subgraph Sources[Data Sources]
        A1[Transactional DBs]
        A2[Event Streams]
        A3[SaaS APIs]
    end

    subgraph Ingestion[Ingestion Layer]
        B1[Kafka/Kinesis]
        B2[Fivetran/Airbyte]
    end

    subgraph Storage[Object Storage]
        C1[S3/ADLS/GCS]
    end

    subgraph Lakehouse[Lakehouse Metadata Layer]
        D1[Delta Lake/Iceberg]
        D2[Transaction Log]
        D3[Schema Registry]
    end

    subgraph Compute[Compute Engines]
        E1[Spark - Batch ETL]
        E2[Trino - Ad-hoc SQL]
        E3[Flink - Streaming]
    end

    subgraph Consumption[Consumption Layer]
        F1[BI Tools - Tableau]
        F2[ML Platforms - Databricks]
        F3[Data Science - Jupyter]
    end

    A1 --> B1
    A2 --> B1
    A3 --> B2
    B1 --> C1
    B2 --> C1
    C1 --> D1
    D1 --> E1
    D1 --> E2
    D1 --> E3
    E1 --> F1
    E2 --> F1
    E1 --> F2
    E3 --> F3

    style Lakehouse fill:#ffd700
      

7. Orchestration & Workflow

7.1 Apache Airflow

Apache Airflow is the most popular open-source orchestration tool. Workflows are defined as Directed Acyclic Graphs (DAGs) in Python code.

Key Concepts

from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime

def extract():
    print("Extracting data...")

def transform():
    print("Transforming data...")

def load():
    print("Loading data...")

dag = DAG(
    'etl_pipeline',
    start_date=datetime(2025, 5, 1),
    schedule_interval='@daily'
)

extract_task = PythonOperator(task_id='extract', python_callable=extract, dag=dag)
transform_task = PythonOperator(task_id='transform', python_callable=transform, dag=dag)
load_task = PythonOperator(task_id='load', python_callable=load, dag=dag)

extract_task >> transform_task >> load_task
graph LR
    A[extract] --> B[transform]
    B --> C[load]

    style A fill:#1a5490
    style B fill:#4a235a
    style C fill:#2d5016
      

Pros & Cons

Pros Cons
Rich ecosystem (1000+ integrations) Steep learning curve
Python-based (flexible) DAGs in code (can get messy)
Strong community Heavy resource usage (scheduler overhead)

7.2 Dagster

Dagster is a modern orchestration framework emphasizing data assets (not just tasks). It tracks data lineage and dependencies.

Key Concepts

from dagster import asset

@asset
def raw_orders():
    return extract_orders()

@asset
def cleaned_orders(raw_orders):
    return clean(raw_orders)

@asset
def sales_report(cleaned_orders):
    return aggregate(cleaned_orders)

Dagster auto-generates a DAG: raw_orders → cleaned_orders → sales_report.

Why Dagster?

7.3 Prefect

Prefect is a workflow orchestration tool with a focus on observability and dynamic workflows.

Key Features

7.4 dbt (Data Build Tool)

dbt is a transformation framework for analytics. It's not a full orchestrator, but handles the "T" in ELT.

Key Concepts

-- models/staging/stg_orders.sql
SELECT
  order_id,
  customer_id,
  order_date,
  total_amount
FROM {{ source('raw', 'orders') }}
WHERE status != 'cancelled'

-- models/marts/sales_by_customer.sql
SELECT
  customer_id,
  SUM(total_amount) AS total_sales
FROM {{ ref('stg_orders') }}
GROUP BY customer_id
graph LR
    A[source: raw.orders] --> B[stg_orders]
    B --> C[sales_by_customer]

    style A fill:#8b4513
    style B fill:#4682b4
    style C fill:#2d5016
      

Why dbt?

Modern Pattern: Airflow/Dagster/Prefect orchestrate dbt jobs. dbt handles SQL transformations, orchestrators handle scheduling, sensors, and cross-system dependencies.

7.5 Orchestration Comparison

Tool Best For Pros Cons
Airflow Complex workflows, many integrations Mature, huge ecosystem Heavy, hard to debug
Dagster Data assets, software engineering teams Type-safe, testable, lineage Newer, smaller community
Prefect Dynamic workflows, observability Easy to use, great UI Less mature than Airflow
dbt SQL transformations in warehouses Analyst-friendly, version control Not a full orchestrator

8. Data Quality & Governance

8.1 Great Expectations

Great Expectations is a Python library for data validation, testing, and profiling.

Key Concepts

import great_expectations as gx

# Create expectation suite
suite = context.create_expectation_suite("orders")

# Define expectations
batch.expect_column_values_to_not_be_null("order_id")
batch.expect_column_values_to_be_unique("order_id")
batch.expect_column_values_to_be_between("total_amount", min_value=0, max_value=1000000)

# Validate
result = context.run_checkpoint("orders_checkpoint")
if not result.success:
    raise Exception("Data quality check failed!")

8.2 Data Contracts

A data contract is an agreement between data producers and consumers about schema, SLAs, and semantics.

Example Contract (YAML)

name: orders_table
schema:
  - name: order_id
    type: bigint
    nullable: false
    unique: true
  - name: customer_id
    type: bigint
    nullable: false
  - name: total_amount
    type: decimal(10,2)
    nullable: false
    min: 0
sla:
  freshness: 1 hour
  availability: 99.9%
owner: payments-team
consumers:
  - analytics-team
  - ml-platform

Why Data Contracts?

8.3 Schema Registries

A schema registry (e.g., Confluent Schema Registry, AWS Glue Data Catalog) stores schemas centrally and enforces compatibility rules.

Key Features

8.4 Data Catalogs

A data catalog is a searchable inventory of datasets, helping users discover and understand data.

Popular Catalogs

Example Catalog Entry

Dataset: analytics.sales_summary
Description: Daily sales aggregated by product
Owner: analytics-team
Freshness: Updated daily at 2 AM UTC
Schema:
  - product_id (bigint)
  - date (date)
  - total_sales (decimal)
Lineage:
  Upstream: raw.orders, raw.products
  Downstream: dashboard.executive_sales
Tags: [pii, gdpr]

8.5 Data Governance Framework

flowchart TB
    A[Data Producers] --> B[Schema Registry - Enforce Schema]
    B --> C[Data Pipeline - Validate Quality]
    C --> D[Data Catalog - Metadata & Lineage]
    D --> E[Access Control - Row/Column-level]
    E --> F[Data Consumers]

    G[Data Contracts] --> B
    H[Great Expectations] --> C
    I[Amundsen/DataHub] --> D
    J[IAM/RBAC] --> E

    style D fill:#ffd700
      

9. Modern Data Stack

9.1 Components

The modern data stack is a set of cloud-native tools optimized for ELT workflows:

9.2 Architecture Diagram

flowchart LR
    subgraph Sources[Data Sources]
        A1[PostgreSQL]
        A2[Salesforce]
        A3[Stripe]
    end

    subgraph Ingestion[Ingestion - Fivetran]
        B1[Connectors]
    end

    subgraph Storage[Data Warehouse - Snowflake]
        C1[Raw Schema]
        C2[Staging Schema]
        C3[Analytics Schema]
    end

    subgraph Transform[Transformation - dbt]
        D1[Models]
        D2[Tests]
        D3[Docs]
    end

    subgraph BI[Business Intelligence]
        E1[Looker]
        E2[Metabase]
    end

    subgraph Reverse[Reverse ETL - Hightouch]
        F1[Sync to Salesforce]
        F2[Sync to HubSpot]
    end

    A1 --> B1
    A2 --> B1
    A3 --> B1
    B1 --> C1
    C1 --> D1
    D1 --> C2
    C2 --> D1
    D1 --> C3
    C3 --> E1
    C3 --> E2
    C3 --> F1
    C3 --> F2

    style Storage fill:#1a5490
    style Transform fill:#2d5016
      

9.3 Modern Data Stack Benefits

Benefit How
Faster Time to Value Plug-and-play connectors, no custom ETL code
Scalability Cloud warehouses scale compute independently
Lower Maintenance Managed services (no infrastructure to manage)
Analyst Empowerment SQL-first tools (dbt) let analysts build pipelines
Version Control dbt models in Git, peer review, CI/CD

9.4 Modern Stack vs Traditional

Dimension Traditional Stack Modern Stack
Ingestion Custom ETL scripts (Python, Java) Fivetran, Airbyte (managed connectors)
Warehouse On-premise (Oracle, Teradata) Cloud (Snowflake, BigQuery, Databricks)
Transformation Stored procedures, ETL tools dbt (SQL, version control, tests)
Orchestration Cron jobs, Control-M Airflow, Dagster, Prefect
BI Tableau, MicroStrategy Looker, Metabase, Mode
Team Data engineers build everything Analysts build with SQL, engineers focus on platform
Modern Stack Philosophy: Buy over build. Use managed services for ingestion (Fivetran), storage (Snowflake), and BI (Looker). Reserve engineering effort for business logic (dbt models) and custom orchestration.

10. Choosing the Right Architecture

10.1 Decision Framework

Choosing between warehouse, lake, or lakehouse depends on:

10.2 Decision Flowchart

flowchart TD
    A[Start: Choose Data Architecture] --> B{Data Types?}
    B -->|Structured only| C[Data Warehouse]
    B -->|All types - structured, semi-, unstructured| D{Primary Use Case?}

    D -->|BI dashboards only| E[Data Warehouse with external tables to lake]
    D -->|ML + Data Science| F{Need ACID guarantees?}

    F -->|Yes - consistency critical| G[Lakehouse - Delta/Iceberg]
    F -->|No - can tolerate eventual consistency| H[Data Lake]

    C --> I{Query Volume?}
    I -->|High - 1000s concurrent| J[Snowflake or BigQuery]
    I -->|Medium - 100s concurrent| K[Redshift or Synapse]
    I -->|Low - batch queries| L[Athena on S3]

    G --> M{Ecosystem?}
    M -->|Databricks-centric| N[Delta Lake]
    M -->|Multi-engine - Trino, Spark, Flink| O[Apache Iceberg]
    M -->|Streaming CDC heavy| P[Apache Hudi]

    H --> Q{File Format?}
    Q -->|Analytics queries| R[Parquet]
    Q -->|Schema evolution| S[Avro]
    Q -->|Legacy exports| T[CSV/JSON]

    style G fill:#ffd700
    style J fill:#1a5490
    style N fill:#2d5016
      

10.3 Sizing Guidelines

Scale Data Volume Team Size Recommendation
Startup < 1 TB 1-5 people PostgreSQL (OLTP) + Metabase (BI)
Small Company 1-10 TB 5-15 people Fivetran + Snowflake + dbt + Looker
Mid-size 10-100 TB 15-50 people Lakehouse (Delta Lake) + dbt + Databricks
Enterprise > 100 TB 50+ people Data Lake (S3) + Lakehouse (Iceberg) + Airflow + Multiple BI tools

10.4 Cost Comparison (Rough Estimates)

Architecture Storage Cost Compute Cost Total (1TB, 100 queries/day)
Snowflake $40/TB/month $2-4/credit (~$2-8/hr) ~$500-1000/month
BigQuery $20/TB/month $5/TB scanned ~$200-500/month
Redshift Included in compute $0.25-2/hr (node-based) ~$180-1440/month
S3 + Athena $23/TB/month $5/TB scanned ~$50-200/month
Databricks S3 ($23/TB/month) DBU pricing (varies) ~$300-800/month
Cost Optimization: Use partitioning and columnar formats (Parquet) to reduce data scanned. BigQuery and Athena charge per TB scanned—bad queries on unpartitioned tables can cost thousands. Snowflake charges per compute time, so optimize query efficiency.

10.5 Real-World Examples

Scenario 1: SaaS Startup (50 employees, 500GB data)

Stack: Fivetran (ingest from Stripe, Salesforce) → Snowflake (warehouse) → dbt (SQL transforms) → Metabase (BI dashboards)

Why: Fast setup, no data team yet, BI-only (no ML)

Scenario 2: E-commerce (500 employees, 50TB data, ML-heavy)

Stack: Kafka → S3 (raw) → Delta Lake (lakehouse) → Spark (ETL) → Databricks (ML platform) + Tableau (BI)

Why: Need both BI and ML, streaming data, large scale

Scenario 3: Financial Services (10,000 employees, 500TB data, strict governance)

Stack: On-premise + cloud hybrid. Teradata (EDW) for regulatory reports, S3 + Iceberg (lakehouse) for analytics, Airflow (orchestration), DataHub (governance)

Why: Compliance requires on-premise, but want cloud for innovation


Summary

Topic Key Takeaways
Warehouse vs Lake vs Lakehouse Warehouse = schema-on-write, structured, fast BI. Lake = schema-on-read, all data types, flexible. Lakehouse = best of both (ACID + flexibility)
ETL vs ELT ETL = transform before loading (traditional). ELT = load raw, transform in warehouse (modern, cloud-native)
Warehouse Architecture Kimball = dimensional (star schema, fast queries). Inmon = normalized (3NF, single source of truth). Medallion = bronze/silver/gold layers
Star Schema Fact table (metrics) + dimension tables (context). Grain defines row meaning. Conformed dimensions enable drill-across
Data Lake Object storage (S3) + schema-on-read. Use zones (raw/cleansed/curated), Parquet for analytics, partition by query filters
Lakehouse Delta Lake (Databricks), Iceberg (multi-engine), Hudi (streaming). ACID + time travel + schema evolution on data lakes
Orchestration Airflow = mature, Python DAGs. Dagster = asset-centric, type-safe. dbt = SQL transformations in warehouse
Quality & Governance Great Expectations = data validation. Data contracts = schema agreements. Catalogs (Amundsen, DataHub) = discovery + lineage
Modern Stack Fivetran (ingestion) → Snowflake (warehouse) → dbt (transform) → Looker (BI). Buy over build, SQL-first
Choosing Architecture BI-only → warehouse. ML + BI → lakehouse. Exploratory → lake. Use decision flowchart based on data types, use case, scale
Interview Cheat Sheet:
  • Warehouse = structured, schema-on-write, BI queries
  • Lake = all types, schema-on-read, ML + exploration
  • Lakehouse = ACID on lakes (Delta/Iceberg/Hudi)
  • ETL = transform before load (old). ELT = load raw, transform in warehouse (modern)
  • Star schema = fact + dimensions, grain matters
  • Modern stack = Fivetran + Snowflake + dbt + Looker
  • Always partition by query filters (date, region)
  • Use columnar formats (Parquet) for analytics

Further Reading