Object Oriented Semantic Layer. Despite their intention, many data… | by Moritz Weidner | Jun, 2026 | MediumSitemapOpen in appSign up<br>Sign in
Medium Logo
Get app<br>Write
Search
Sign up<br>Sign in
Press enter or click to view image in full size
Object Oriented Semantic Layer
Moritz Weidner
9 min read·<br>Just now
Listen
Share
Despite their intention, many data warehouse semantic layers in use today are cumbersome and lack any real abstraction. In this article I show how encoding your metrics as objects in your warehouse will fundamentally re-approach your semantic layer in a way that is both scalable and usable, reducing bloat and vastly improving ease of use. The result is a metric that knows its own aggregation, carries its own date context, and resolves correctly inside any SQL query with a single function call. There is no query builder, no API, no separate service standing between the analyst and the answer.
The Issue With Current Semantic Layer Solutions<br>Most semantic layers fundamentally act as query builders. Take DBT’s semantic layer solution: Metric Flow. First, key components such as relevant dimensions and aggregation type are defined. This typically has to happen on a per-model basis. Already a problem arises: if you have to define each metric per model, there is absolutely zero centralization of metric definitions in your warehouse. A model owned by a different team may see drift in metric definitions, especially when branching marts or ad-hoc tables are used to serve specific business needs.<br>The defined metric components don’t act as the metric or query itself, rather a “run” of the semantic tool needs to be triggered to build a query that acts as the semantic layer. These built queries can get messy quickly. With the intention to exhaustively calculate the relevant metrics for all possible aggregation levels, semantic models can reach many thousands of lines of SQL. The problem is apparent: semantic models are an auditing nightmare. With the complexity of the semantic query you rely on reading only input and output of the semantic layer. The layer itself becomes a black-box. Most aggravating, a query builder is the exact opposite of abstraction. Now metrics live in messy sub-queries rather than mathematically abstract calculations. The analytics engineer relies on the semantic tool correctly interpreting the mathematical intention of a calculation rather than fully defining the calculation themselves.<br>Beyond the architectural concerns there are more practical concerns. Semantic layers often seem like a great idea: a layer that “magically” calculates the relevant metric to reduce manual work and foster alignment of metrics. In practice, however, it invites metric bloat or high maintenance cost to keep the layer trimmed and functional.<br>The solution to these semantic layer issues needs to be able to centralize all metrics across an entire warehouse, ensure metrics are easily maintained and audited, and users can easily use and access metrics where needed.<br>Native semantic layer solutions now exist that tackle the same core problem of centralizing metric definitions at the warehouse level. Snowflake Semantic Views is the closest comparison. The difference is in how analysts actually interact with it. Snowflake Semantic Views introduce their own query syntax, require BI tool integrations to reach non-SQL users, and sit as a separate layer above your marts.<br>OOSL does none of that. A metric object is just a column on a view. METRIC() is just a function call. Any analyst who can write a GROUP BY can use it: in a Snowflake worksheet, a dbt model, a notebook, or a BI tool’s custom SQL field. Without learning new syntax, without hitting an API, and even without any knowledge of how the metric is defined underneath. The goal is a semantic layer that gets used in everyday queries, not one that is architecturally complete but adds a complex step every time someone needs a number.<br>Metrics as Warehouse Objects<br>The principle idea of OOSL is to encode metrics as warehouse objects. Metric definitions compile once into the warehouse, as columns on a mart, with the express goal of resolving these objects at query runtime.<br>Metrics get defined as macros, within this macro we store key information on how the metric is calculated, plus any other additional information that may prove useful. This includes:<br>a SQL blurb to define the relevant logic for a metrics<br>The aggregation type for the metric<br>The name of the relevant date field for the metric to aggregate on<br>Plus any other information including but not limited to: metric descriptions, additional logic to avoid abuse of the metrics, etc.<br>{% macro mtr_converted_carts() %}<br>OBJECT_CONSTRUCT(<br>'value', CASE WHEN is_converted THEN 1 ELSE 0 END,<br>'agg', 'AVG',<br>'date', SESSION_TS<br>{% endmacro %}Metrics will be written into a mart. The metric is called as a macro, dbt expands it into an OBJECT_CONSTRUCT and materializes it as a column on a view.<br>--mart_jaffle_shop<br>SELECT<br>,...