Skip to content
Reliable Data Engineering
Lesson
Open in the interactive app

Modern Data Modeling

The modern stack (cheap columnar storage, elastic compute, dbt, lakehouses) changed where modeling effort pays off, but not why we model: correct, understandable, reusable data.


1. The big methodologies

Kimball (dimensional)Inmon (CIF)Data Vault 2.0One Big Table / wide tables
Core ideaBusiness-process stars with conformed dims, bottom-upNormalised enterprise DWH (3NF), marts downstreamHubs (keys), Links (relationships), Satellites (history), insert-onlyPre-joined denormalised tables per use case
StrengthUsable by analysts, fast BISingle integrated truthAuditability, many changing sources, parallel loading, history of everythingSimplicity, BI speed on columnar engines
WeaknessIntegration across many sources takes designSlow to deliver, complexNot query-friendly: needs a dimensional/business layer on topDuplication, metric drift, costly rebuilds
Typical placeGold / martsLegacy enterprise DWHsSilver/integration layer in regulated enterprisesServing layer for dashboards / ML

Pragmatic modern answer: sources → bronze (raw) → silver (cleaned, conformed entities; sometimes Data Vault in regulated, multi-source enterprises) → gold (Kimball stars) → optional OBTs/semantic layer for consumption.


2. Data Vault 2.0

erDiagram
    HUB_CUSTOMER ||--o{ SAT_CUSTOMER_CRM : "history"
    HUB_CUSTOMER ||--o{ SAT_CUSTOMER_WEB : "history"
    HUB_CUSTOMER ||--o{ LINK_CUSTOMER_ORDER : ""
    HUB_ORDER ||--o{ LINK_CUSTOMER_ORDER : ""
    HUB_ORDER ||--o{ SAT_ORDER : "history"
    HUB_CUSTOMER {
        string customer_hk PK "hash of business key"
        string customer_bk "business key"
        timestamp load_dts
        string record_source
    }
    SAT_CUSTOMER_CRM {
        string customer_hk FK
        timestamp load_dts PK
        string hash_diff "change detection"
        string name
        string segment
        string record_source
    }
    SAT_CUSTOMER_WEB {
        string customer_hk FK
        timestamp load_dts PK
        string hash_diff
        string preferred_language
    }
    HUB_ORDER {
        string order_hk PK
        string order_bk
        timestamp load_dts
        string record_source
    }
    LINK_CUSTOMER_ORDER {
        string link_hk PK
        string customer_hk FK
        string order_hk FK
        timestamp load_dts
        string record_source
    }
    SAT_ORDER {
        string order_hk FK
        timestamp load_dts PK
        string hash_diff
        string status
        decimal amount
    }
ComponentContainsChanges
HubUnique business keys of a core concept (customer, order) + hash keyInsert-only, one row per key ever seen
LinkRelationships between hubs (customer–order)Insert-only
SatelliteDescriptive attributes + history, per source / rate of changeInsert a row when hash_diff changes
(Business vault)Derived/computed structures, PIT and bridge tables for performance

Why people choose it: dozens of sources that change often, strict audit (“show me exactly what system X said on date Y”), parallel loading (hash keys, no lookups), insert-only (no updates → easy restatement). Why people avoid it: 3× more tables, needs a dimensional layer for consumption, PIT (point-in-time) tables to make joins tolerable, and a team that knows the method.


3. One Big Table (OBT) in the lakehouse

CREATE OR REPLACE TABLE gold.obt_orders AS
SELECT f.*, c.segment, c.city, c.country, p.category, p.brand, d.iso_week, d.month_name, s.region
FROM gold.fct_order_lines f
JOIN gold.dim_customer c ON f.customer_key = c.customer_key
JOIN gold.dim_product  p ON f.product_key  = p.product_key
JOIN gold.dim_date     d ON f.order_date_key = d.date_key
JOIN gold.dim_store    s ON f.store_key    = s.store_key;

4. Modeling event data

Event streams (clickstream, IoT, app logs) have many event types with different properties.

ApproachShapeProsCons
One table per event typefct_page_view, fct_add_to_cartClean schemas, typedHundreds of tables, cross-event analysis needs unions
Single wide events table + semi-structured propertiesevents(event_id, user_id, ts, event_type, properties VARIANT/MAP)Flexible, one table to queryWeak typing, property drift, JSON extraction cost
Hybrid (common)Common columns typed + properties map; promote hot properties to columns in silverBalanceNeeds governance (tracking plan)
Activity schemaOne narrow activity_stream(entity_id, ts, activity, feature_1..3, revenue_impact, link)Any customer-journey question with self-joins on one tableUnfamiliar; extra modeling discipline

Always include: event_id (dedup), event_ts (client), received_ts (server), schema_version, source/app version.

5. Semi-structured & nested data

6. Modeling for streaming

7. dbt project layering

flowchart LR
    SRC[(sources)] --> STG["staging<br/>stg_crm__customers<br/>1:1 with source, rename, cast, dedupe"]
    STG --> INT["intermediate<br/>int_orders_joined<br/>business logic, not exposed"]
    INT --> MART["marts<br/>dim_customer, fct_orders<br/>contracts + tests + docs"]
    MART --> SEM["semantic layer / metrics<br/>revenue, active_users"]
    SEM --> BI[BI / apps]

Conventions: stg_<source>__<entity>, int_<verb/description>, dim_/fct_; tests on every primary key (unique + not_null) and foreign key (relationships); model contracts on public marts; exposures for dashboards.

8. Semantic / metrics layer

Problem: “revenue” is defined differently in 12 dashboards. Solution: define metrics once (measure + aggregation + filters + dimensions allowed) in a semantic layer (dbt Semantic Layer/MetricFlow, Databricks metric views, Cube, LookML, AtScale), and let BI tools query metrics instead of raw tables.

9. Choosing: a decision guide

SituationModel
Analytics for a product team, few sourcesKimball stars (+ OBTs for dashboards)
Many volatile sources, regulated, audit everythingData Vault in silver, Kimball in gold
Exploratory, small team, speed over purityWide tables built in dbt, refactor to stars as reuse emerges
Customer-journey analytics across many event typesActivity schema or hybrid events table
ML feature engineeringEntity-centric feature tables (one row per entity per timestamp)

10. Interview questions

Kimball or Data Vault for a new lakehouse with 40 source systems in a bank?

Likely Data Vault (or a vault-inspired integration layer) in silver for auditability, parallel loading and absorbing source changes without remodeling, with Kimball marts in gold for consumption. With few sources and a small team, a vault’s overhead isn’t justified; go straight to conformed silver entities + stars.

Is dimensional modeling obsolete with columnar warehouses and OBTs?

No. Columnar engines reduce the performance need for stars, but not the need for clear grain, conformed definitions, history handling and reuse. OBTs built from a governed star give both speed and consistency; OBTs without a core model drift and duplicate logic.

How do you model an events table where each event type has different properties?

Typed common columns (event_id, user_id, ts, event_type, session_id, app_version) plus a map/variant for properties; promote frequently used properties to typed columns in silver or per-event-type tables for hot events; enforce a tracking plan (schemas per event type) in a registry so properties don’t drift.