DBTScale & Expertise

The Semantic Layer and MetricFlow

How analytics engineers turn raw warehouse tables into trusted models — pick a topic on the left and its full breakdown loads here: the mental model, the compiled SQL dbt actually issues, live runs you can drive yourself, and the failure modes that quietly ship wrong numbers.

18 min readTopics chapter readerLevel · Hard
01 · The same word, four honest answers

Four Numbers, One Question

A metric is not a SQL query with a name attached to it. It is a declaration — which measure, aggregated how, over what time — that every tool asks the SAME question of, so "July revenue" stops meaning whatever the last person who wrote the query meant.

4 min · Topic 1 of 9

Chapter 11 shipped an exposure named finance_revenue_extract with a name that admits the problem out loud: a consumer reads raw Stripe directly, bypassing every model you own, to reconcile modelled revenue against the source system. This chapter is that reconciliation, and it starts by making the disagreement arithmetic rather than political.

Below are four numbers, all real, all computed correctly, all answering "what was Wavelength's July revenue?" Attribute each one before you see why it differs from the $184.00 the mart already ships.

Four teams, four numbers, one question — commit before anything is revealedEvery number below is real Wavelength July data, computed correctly. $184.00 is the baseline: cash collected, 5 succeeded stripe.payments rows. For each of the other three, say which SINGLE axis explains why it differs — a different EVENT counted as “revenue happened,” a different CLOCK (when it happened), or a different UNIT (what kind of number it is).
$184.00 — baseline$184.00analytics engineering, the mart · 5 succeeded payments, cash date
$192.00RevOps / Billing
$37.34Finance — recognised revenue
$28.00Finance — MRR
Core mental model

A metric is not a SQL query with a name attached to it. It is a declaration — which measure, aggregated how, over what time — that every tool asks the SAME question of, so "July revenue" stops meaning whatever the last person who wrote the query meant.

Why it matters

Four correct numbers for one business question is not a data-quality bug — every one of $184.00, $192.00, $37.34 and $28.00 is arithmetically right, backed by a real, hand-verifiable calculation over Wavelength's own rows (two are a straight query; $37.34 additionally needs a stated service-period convention no shipped table carries on its own). What is missing is not accuracy; it is a place to write down WHICH question each number answers, once, so "RevOps's July number" and "Finance's July number" stop being two undocumented queries that happen to disagree. That place is the semantic layer, and this chapter builds it on Wavelength's own two fact tables.

Semantic layer
A declared, queryable layer between the warehouse and every tool that reads it — metrics defined once, resolved to SQL at query time rather than baked into a table. This chapter is the dbt implementation; the concept and the term MetricFlow belong to data-modeling/modern-warehouse-modeling.
MetricFlow
The query engine that compiles a metric request into SQL. Relicensed BSL → Apache 2.0 on 2025-10-14, announced at Coalesce 2025 — that changed the LICENCE, not what ran on Core: defining metrics and running mf query already worked on dbt Core under the BSL. What has always needed the dbt platform is the hosted Semantic Layer (the APIs a BI tool actually connects through) — that was true before 2025-10-14 and is still true after it.
dbt platform
What used to be called dbt Cloud. It hosts the Semantic Layer APIs, scheduled exports, and caching — the parts of this chapter that are NOT free on Core. Chapter 1 already lists "the Semantic Layer" among the platform's offerings; that is the hosted API, not the engine.
The three axes a revenue number can differ on
Which EVENT counts as revenue happening, which CLOCK it is dated by, and which UNIT it is reported in. None of the four numbers below is wrong — each picks a different answer on exactly one axis.
The three axes, in general
AxisQuestion it answersThis chapter's instance
EventWhat counts as "it happened"?a succeeded charge vs. any charge attempt
ClockWhen did it happen?the cash date (paid_on) vs. the service period recognised
UnitWhat kind of number is this?a period total vs. a run-rate snapshot (MRR)
Common mistake

Treating a metric mismatch as a data-quality bug and assigning someone to "fix" one of the numbers. There is nothing to fix — $184.00, $192.00, $37.34 and $28.00 are all correct. Chasing one of them to zero just produces a fifth number nobody asked for. The fix is naming what each one answers, not converging them.

Baking one team's filter (status = 'succeeded') permanently into a shared model to "settle" the argument. It settles the mart's number and un-settles everyone else's. The billing question ($192.00) needed the failed charge; bake the filter in and there is no way to un-bake it — someone defines a second model to ask the other question, and the drift this chapter exists to end restarts one layer down.

Better habit

When two teams report different numbers for "the same" metric, ask which axis differs before assuming either one is wrong.

Write down the axis, not just the value, when you publish a number — "$184.00, cash collected" survives a reconciliation conversation; "$184.00" does not.

Treat a self-serve BI proxy query (status ≠ succeeded for "billed") as a proxy, and say so — it is not the source system's own definition.

The semantic layer concept belongs to Data Modeling

data-modeling/modern-warehouse-modeling already owns the semantic-layer concept, the metric-drift argument in general, and the term MetricFlow. This chapter recaps that in two sentences and spends its words on the dbt implementation: the actual YAML, the actual generated SQL, and the drift made arithmetically real on Wavelength's own shipped rows.

Modern warehouse modeling — Data Modeling KB
"Why do teams end up with different numbers for the same metric?"

Weak answers say "bad data" or "someone made a mistake." A strong answer names an axis: a different event counted, a different clock used, or a different unit reported — with a real example on each. Then add the senior point: the fix is not picking a winner, it is declaring the metric once, with its axis choices explicit, so every tool asks the same question.

What finance_revenue_extract was waiting for

Chapter 11 shipped that exposure with an explicit warning: a consumer reads raw Stripe directly, bypassing every dbt model, specifically to reconcile modelled revenue against the source system. This chapter is the other side of that reconciliation — the semantic layer does not replace the raw-Stripe check, but it does mean the modelled side stops being a single undocumented query.

Remember this

Four numbers, four correct answers, three axes — event, clock, unit — and the fix is declaring which axis each one picked, once, not converging them into a fifth number nobody asked for.