Semantic Layer
A semantic layer is where business concepts and metrics are defined for reuse, often in version-controlled configuration, and from which queries are generated for whatever tool asks: a dashboard, a notebook, an API, an export. Cube states the aim as building “a reusable semantic layer that provides the shared context for AI agents, BI dashboards, and embedded analytics.”
The problem it solves is duplication of meaning. Without one, the definition of “active user” lives inside every dashboard that reports it โ a filter someone wrote, copied, and adjusted. Seventeen dashboards hold seventeen definitions that agree by accident, and a change requires finding all of them.
What it contains
The vocabulary differs by product and the structure does not, and it maps onto dimensional modeling closely enough that anyone who has worked with fact and dimension tables will recognize it.
- Entities and joins. dbt semantic models declare entities and their key types so the engine can choose supported relationships.
- Dimensions. Attributes used to group and filter results, such as region, product category, and time.
- Measures and simple metrics. Aggregated expressions, such as sales amounts or unique visits; exact terminology and supported definitions vary by product version.
- Metrics. Named, documented combinations of measures with filters and a type, which is what consumers actually request.
- Presentation metadata. Kept separate from meaning on purpose: dbt’s
label“Defines the display value in downstream tools,” so changing only the display label leaves the value unchanged for the same query context.
Defining dbt metrics in YAML makes the logic easy to version and review. Enforce review through the required approval and deployment process rather than assuming every configuration edit passes through a reviewer. Record the version actually deployed and when consumers began using it; repository history alone does not identify the definition behind a past result.
Generated joins and their data assumptions
MetricFlow generates SQL using declared entity types and relationships to choose supported join paths. This can prevent classes of fan-out and chasm joins when the model and data satisfy those declarations.
Declaring the grain and join keys makes the intended relationship reviewable; it does not enforce the contents of every input. Check primary or unique keys for duplicates and nulls, required relationships for missing matches, and historical lookups for exactly the intended version. Reconcile totals at a comparable grain. dbt separates semantic configuration validation and SQL execution checks from data tests, such as uniqueness and relationship assertions.
What it does not solve
Three limits are worth knowing before the project is justified on the wrong grounds.
- Which definition is correct. The layer serves whichever denominator you declared, faithfully. Deciding it is a business judgment needing a named owner, and an unowned metric definition in a semantic layer is just a tidier version of the same argument.
- Consumers who go around it. An independently maintained spreadsheet calculation can diverge from the shared definition; bypassing the layer does not itself prove disagreement. Track adoption and how other approved implementations stay aligned.
- Dimensions that disagree. If “region” means one thing in sales and another in support, a shared metric grouped by region is incoherent no matter how it is served. That is what conformed dimensions address, and it is upstream of this layer.
What it assumes about your warehouse
MetricFlow accepts inputs ranging from raw to fully denormalized datasets. Its documentation recommends well-modeled inputs and warns about redundancy and restricted aggregation options; that is not a requirement that every semantic engine receive normalized tables. Assess whether the input retains the keys, rows, and attributes needed for the supported questions.
A wide table formed by copying a unique customer attribute onto each order can preserve every order and its reporting detail. A summary replacing those orders, or an inner join that drops unmatched ones, loses information. Evaluate these effects separately from redundancy and refresh cost when considering denormalization; column count alone does not establish a loss of granularity.
Caching and pre-aggregation can reduce repeated computation, but their refresh schedule is only one part of freshness. A cache rebuilt at 10:00 from a source current through yesterday still contains yesterdayโs coverage. Record refresh time, source coverage, the requested as-of period, and acceptable delay separately. A retained approved snapshot can be appropriate when its status is visible; a result that misses its freshness contract or is mislabeled as current is the failure.
Consumer-facing abstraction helps here too โ Cube’s views give “a layer of abstraction that makes it easier to manage changes to the underlying data model” โ which is what lets the physical tables be reshaped without breaking every report. Where consumers are other teams, pair that with a data contract stating what may change and with what notice.
Track the share of reporting served through the layer and disagreement between reports using comparable definition versions, input coverage, populations, filters, and access contexts. These are diagnostic signals, not proof of correctness or of a single cause. How the layer fits with definitions, change management, and the boundary between central and team metrics is worked through in Whose Revenue Number Is Right?.
References:
- dbt Documentation, About MetricFlow
- dbt Documentation, Creating metrics
- Cube Documentation, Data modeling overview
- dbt, Semantic Layer validations
- dbt, Data tests
- Cube, Refreshing pre-aggregations
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
