Semantic Models
Semantic models are the bridge between the Gold Warehouse (dbt output) and Power BI reports. They define the business-facing schema — tables, columns, relationships, measures, and display names — that reports bind to. All models are stored as TMDL (Tabular Model Definition Language) in workspaces/semantic/<ModelName>.SemanticModel/.
Model Inventory
| Model | Mode | Purpose | Source |
|---|---|---|---|
| LH_Gold_Full | DirectLake | Primary model — all dimensions and facts | Gold_Warehouse (all mart tables) |
| CheeseFuturePricesDashboard | DirectLake | Cheese futures pricing | Gold_Warehouse (market data tables) |
| Datacollect | Import | Data collection forms and submissions | Gold_Warehouse (datacollect tables) |
| ETL_Monitoring | Import | Pipeline run metrics, CU utilization | Gold_Warehouse (monitoring tables) |
| Report Owners | Import | Report ownership metadata tracking | Gold_Warehouse (metadata tables) |
DirectLake models read directly from Delta tables in Gold_Warehouse without importing data into memory. They auto-refresh whenever dbt completes a build — no scheduled refresh is needed. Import models pull data on a schedule and require OAuth credentials owned by geris_fabric_admin@geris.nl.
TMDL File Structure
Each semantic model directory contains:
LH_Gold_Full.SemanticModel/
model.tmdl # Master file with ref table entries
relationships.tmdl # All table relationships
metrics.tmdl # DAX measures (if any)
dim_customer.tmdl # Per-table file: columns, partitions, measures
dim_product.tmdl
fact_trade.tmdl
...
model.tmdl
The master file lists all tables in the model via ref table entries. When adding or removing a table, this file must be updated alongside the table's .tmdl file.
relationships.tmdl
Defines all relationships between tables. Key rules:
- When a fact table has both a direct and an indirect path to a dimension, one relationship must be marked
isActive: falseto avoid ambiguity - Relationship names follow the pattern
<fact_column>_to_<dim_table>
Per-table .tmdl files
Each table file defines:
- Columns:
sourceColumnmust match the dbt output column name exactly (snake_case) - Partition: DirectLake tables have a
partitionblock withmode: directLake,entityName(the warehouse table name),schemaName: dbo, andexpressionSource: DatabaseQuery - Measures: DAX expressions for calculated metrics
- Hierarchies: Drill-down paths (e.g., Year > Quarter > Month)
TMDL Conventions and Rules
Description Syntax
TMDL uses triple-slash (///) for descriptions, NOT a description: key-value property. Using description: "text" causes a Workload_FailedToParseFile error.
/// Customer dimension table with all active customers
table dim_customer
- No blank line allowed between
///and the object declaration - Multi-line descriptions use consecutive
///lines - Table descriptions:
///at 0-tab indent, abovetable X - Column descriptions:
///at 1-tab indent, above the column declaration
Column Naming
sourceColumnmust match dbt output column names exactly (snake_case)- Display names must also be snake_case — no
&or special characters - Renaming a dbt column requires updating
sourceColumnin every model that references it
DirectLake Constraints
- DirectLake does NOT support calculated columns — all computed values must be in dbt
- A DirectLake table with ONLY measures and no columns fails to load — always include at least one column
- The
metricstable in the warehouse (SELECT NULL AS dummy WHERE 1=0) is an empty container that works because the TMDL defines adummycolumn
DAX Reserved Words
Always quote table names that are DAX reserved keywords with single quotes: 'refresh'[column]. Common reserved words: refresh, date, time, year, month, day, table, column, measure, value. Unquoted reserved words cause opaque SYNTAXERROR errors.
Two-Pass Deployment for New Models
When adding a NEW semantic model + report pair, the first deployment will fail because the report needs a semanticmodelid GUID that does not exist until the semantic model is deployed.
Process:
- Use a placeholder GUID (e.g.,
00000000-0000-0000-0000-000000000000) indefinition.pbir - Push and run
fabric-deploy— the semantic model deploys, the report fails withPowerBIEntityNotFound - Query the actual GUID:
GET /v1/workspaces/\{semantic_workspace_id\}/items?type=SemanticModel - Update
definition.pbirwith the real GUID - Add find/replace entry to
deployment/parameter-ENV.yml - Push and re-run
fabric-deploy— both deploy successfully
This is expected behavior, not a bug. Plan for two deploys on first creation.
GUID Substitution
Each environment (DEV, UAT, PROD) has different semantic model GUIDs. The deployment/parameter-ENV.yml files contain find/replace entries that fabric-cicd uses during deployment to substitute the correct GUIDs in report connectionString fields.
The connection_overrides.semantic_model_ids section in deployment/ENV.yml maps the git-committed GUID (typically DEV) to the environment-specific GUID.
Ownership and Licensing
Semantic models require an owner with a Fabric Pro license for scheduled refresh and credential management. SPNs cannot own semantic models because they lack Pro licenses.
| Identity | Role | License |
|---|---|---|
geris_fabric_admin@geris.nl | Semantic model owner | Fabric Pro (required) |
sp-fabric-platform-admin | Deploys models via fabric-cicd | None (API-only — cannot own) |
Import models (Datacollect, ETL_Monitoring, Report Owners) require OAuth credentials owned by geris_fabric_admin@geris.nl. After deployment to a new environment, the owner must manually "Take over" credentials in the Fabric portal (Settings > Scheduled refresh > Data source credentials).
DirectLake models auto-refresh from Gold Warehouse and do not require credential configuration.
Environments
| Environment | Branch | Workspace |
|---|---|---|
| DEV | main | DEV-Semantic |
| UAT | release-uat | UAT-Semantic |
| PROD | release-prod | PROD-Semantic |
Related Pages
- Reporting Overview — report structure and access model
- Report Catalog — full report inventory
- Troubleshooting — TMDL gotchas and DirectLake issues
- ADR Decision Log — 4-workspace layout decision