Development Documentation
View as:

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

ModelModePurposeSource
LH_Gold_FullDirectLakePrimary model — all dimensions and factsGold_Warehouse (all mart tables)
CheeseFuturePricesDashboardDirectLakeCheese futures pricingGold_Warehouse (market data tables)
DatacollectImportData collection forms and submissionsGold_Warehouse (datacollect tables)
ETL_MonitoringImportPipeline run metrics, CU utilizationGold_Warehouse (monitoring tables)
Report OwnersImportReport ownership metadata trackingGold_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: false to avoid ambiguity
  • Relationship names follow the pattern <fact_column>_to_<dim_table>

Per-table .tmdl files

Each table file defines:

  • Columns: sourceColumn must match the dbt output column name exactly (snake_case)
  • Partition: DirectLake tables have a partition block with mode: directLake, entityName (the warehouse table name), schemaName: dbo, and expressionSource: 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, above table X
  • Column descriptions: /// at 1-tab indent, above the column declaration

Column Naming

  • sourceColumn must 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 sourceColumn in 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 metrics table in the warehouse (SELECT NULL AS dummy WHERE 1=0) is an empty container that works because the TMDL defines a dummy column

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:

  1. Use a placeholder GUID (e.g., 00000000-0000-0000-0000-000000000000) in definition.pbir
  2. Push and run fabric-deploy — the semantic model deploys, the report fails with PowerBIEntityNotFound
  3. Query the actual GUID: GET /v1/workspaces/\{semantic_workspace_id\}/items?type=SemanticModel
  4. Update definition.pbir with the real GUID
  5. Add find/replace entry to deployment/parameter-ENV.yml
  6. 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.

IdentityRoleLicense
geris_fabric_admin@geris.nlSemantic model ownerFabric Pro (required)
sp-fabric-platform-adminDeploys models via fabric-cicdNone (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

EnvironmentBranchWorkspace
DEVmainDEV-Semantic
UATrelease-uatUAT-Semantic
PRODrelease-prodPROD-Semantic

Related Pages