erDiagram
dim_customer {
bigint customer_id PK
string name
string segment
string country
}
dim_product {
bigint product_id PK
string name
string category
string brand
}
dim_date {
int date_id PK
date full_date
int month
int year
}
fact_sales {
bigint sale_id PK
bigint customer_id FK
bigint product_id FK
int date_id FK
decimal amount
int quantity
}
dim_customer ||--o{ fact_sales : ""
dim_product ||--o{ fact_sales : ""
dim_date ||--o{ fact_sales : ""
Medallion vs Data Vault vs Kimball: when to use each one and why it matters
Every time you start a data project, the first architectural question is: how do I model the data? And the answer I hear most often is “Medallion, obviously”. But it’s not always the best choice.
In this post I compare the three most widely used approaches in the industry, with real trade-offs and no snake oil.
The problem
You have raw data from multiple sources and you need to get it to a state that analysts, data scientists, and dashboards can consume. How do you organize the intermediate layers?
Kimball (Dimensional Modeling)
The grandfather of data modeling. Ralph Kimball published it in the 90s and it’s still going strong.
The idea
You organize the data into fact tables and dimension tables. Facts are the metrics (sales, clicks, transactions); dimensions are the context (who, when, where, what).
When to use it
- Classic reporting: BI dashboards, KPIs, dimensional analysis
- Teams of analysts who use SQL: the star schema is intuitive, the JOINs are simple
- Stable requirements: you know what questions you’ll be asked
When NOT to use it
- When sources change frequently (Kimball assumes stable schemas)
- When you need a complete audit trail of change history
- When you have 50+ sources with complex relationships
On Databricks
-- Fact table
CREATE TABLE gold.fact_sales (
sale_id BIGINT,
customer_id BIGINT,
product_id BIGINT,
date_id INT,
amount DECIMAL(18,2),
quantity INT
)
USING DELTA
CLUSTER BY (date_id, customer_id);
-- Dimension table with SCD Type 2
CREATE TABLE gold.dim_customer (
customer_sk BIGINT GENERATED ALWAYS AS IDENTITY,
customer_id BIGINT,
name STRING,
segment STRING,
country STRING,
valid_from DATE,
valid_to DATE,
is_current BOOLEAN
)
USING DELTA;Data Vault 2.0
Invented by Dan Linstedt. It’s the most robust approach for companies with many sources and audit requirements.
The idea
Three types of tables: Hubs (business entities), Links (relationships between hubs), and Satellites (descriptive attributes with history).
erDiagram
hub_customer {
string hash_key PK
bigint customer_id
timestamp load_date
string record_source
}
hub_product {
string hash_key PK
bigint product_id
timestamp load_date
string record_source
}
link_sale {
string hash_key PK
string customer_hk FK
string product_hk FK
timestamp load_date
string record_source
}
sat_customer {
string hash_key FK
string name
string segment
string hash_diff
timestamp load_date
}
sat_product {
string hash_key FK
string name
string category
string hash_diff
timestamp load_date
}
hub_customer ||--o{ link_sale : ""
hub_product ||--o{ link_sale : ""
hub_customer ||--o{ sat_customer : ""
hub_product ||--o{ sat_product : ""
When to use it
- Many heterogeneous sources (50+): Data Vault doesn’t break when you add a new source
- Audit and compliance: every record has
load_dateandrecord_source, so you know exactly where each piece of data came from - Large teams: it parallelizes well; each developer can work on a Hub/Link without stepping on anyone else
- Schemas that change frequently: adding an attribute means creating a new Satellite, not altering existing tables
When NOT to use it
- Small teams (< 5 people): the overhead of maintaining Hubs/Links/Satellites isn’t worth it
- Fast projects or POCs: too much ceremony
- If your analysts are going to run SQL directly against the vault (it’s ugly to consume without a presentation layer)
On Databricks
-- Hub
CREATE TABLE vault.hub_customer (
customer_hk STRING, -- hash of the business key
customer_id BIGINT,
load_date TIMESTAMP,
record_source STRING
)
USING DELTA;
-- Satellite
CREATE TABLE vault.sat_customer (
customer_hk STRING,
name STRING,
segment STRING,
country STRING,
hash_diff STRING, -- hash of the attributes to detect changes
load_date TIMESTAMP,
record_source STRING
)
USING DELTA;
-- Link
CREATE TABLE vault.link_sale (
sale_hk STRING,
customer_hk STRING,
product_hk STRING,
load_date TIMESTAMP,
record_source STRING
)
USING DELTA;Medallion (Bronze / Silver / Gold)
The approach popularized by Databricks. It’s not a data model in the Kimball or Data Vault sense — it’s a layered architecture.
The idea
The key point many people miss
Medallion doesn’t tell you how to model Gold. It only tells you there are layers. In Gold you can use Kimball, Data Vault, or flat tables. The modeling decision is still yours.
When to use it
- Always, as a layered architecture: it’s an organizational pattern, it doesn’t compete with Kimball or Data Vault
- Teams that are just getting started: it’s simple to understand and implement
- Projects on Databricks/Delta Lake: it’s optimized for the ecosystem
When NOT to use it (on its own)
- When you need formal auditing (Medallion has no native
record_source) - When Gold needs serious dimensional modeling (that’s where you combine it with Kimball)
On Databricks
-- Bronze: raw data
CREATE TABLE bronze.raw_transactions (
_ingest_timestamp TIMESTAMP DEFAULT current_timestamp(),
_source_file STRING,
payload STRING -- raw JSON
)
USING DELTA;
-- Silver: clean and typed
CREATE TABLE silver.transactions (
transaction_id BIGINT,
customer_id BIGINT,
amount DECIMAL(18,2),
transaction_date DATE,
status STRING,
_silver_timestamp TIMESTAMP DEFAULT current_timestamp()
)
USING DELTA
CLUSTER BY (transaction_date);
-- Gold: business model (here you choose Kimball, flat, etc.)
CREATE TABLE gold.daily_revenue (
date DATE,
segment STRING,
total_revenue DECIMAL(18,2),
transaction_count BIGINT,
avg_ticket DECIMAL(18,2)
)
USING DELTA;The comparison
| Criterion | Kimball | Data Vault | Medallion |
|---|---|---|---|
| Complexity | Medium | High | Low |
| Auditability | Limited (SCD) | Complete | Not native |
| Source scalability | Medium | High | High |
| Learning curve | Medium | High | Low |
| Analyst consumption | Excellent | Poor (without a layer) | Depends on Gold |
| Flexibility to change | Low | High | High |
| Best for | Classic BI | Enterprise, compliance | Lakehouse, startups |
My recommendation
They’re not mutually exclusive. The pattern that works best in practice:
Medallion as the layered architecture + Kimball in Gold for consumption.
Sources → Bronze (raw) → Silver (clean) → Gold (Kimball star schema)
If you’re in an enterprise context with 50+ sources and audit requirements:
Medallion + Data Vault in Silver + Kimball in Gold.
Sources → Bronze (raw) → Silver (Data Vault) → Gold (Kimball)
And if you’re on a small team building an MVP:
Medallion with flat Gold. No ceremony.
Sources → Bronze → Silver → Gold (simple aggregated tables)
The important thing is understanding that each approach solves a different problem. There’s no universal answer — there’s context.
Links
- The Data Warehouse Toolkit (Kimball) — the foundational book
- Data Vault 2.0 (Dan Linstedt) — the official reference
Medallion Architecture (Databricks) — official documentation
Next week: Data Contracts — how to design a framework from scratch.
