In the legacy format, semantic models were top-level resources, and dbt’s best-practice guide suggested a parallel folder of sem_*.yml files. The latest YAML spec changes the constraint: the semantic model is part of the model’s own YAML entry, and dbt allows only one entry per model. The layout follows from that.

Copy and paste

models/
  marts/
    orders.sql
    orders.yml            # columns, tests, entities, dimensions, metrics of orders
    customers.sql
    customers.yml
  metrics/
    company_kpis.yml      # metrics that combine several semantic models
  saved_queries/
    finance.yml           # saved queries and their exports
  utilities/
    time_spine_daily.sql
    time_spine_daily.yml  # time_spine: config and custom granularities

A cross-model metric in its own file:

# models/metrics/company_kpis.yml
metrics:
  - name: revenue_per_signup
    label: Revenue per signup
    type: ratio
    numerator: revenue      # defined on orders
    denominator: signups    # defined on customers

One entry per model

Entities, dimensions and single-model metrics sit under the model, next to its column descriptions and tests. Describing orders in a second file, for example to keep the semantic part apart, stops the parse:

Compilation Error
  dbt found two schema.yml entries for the same resource named orders. Resources and their associated
  columns may only be described a single time. To fix this, remove the resource entry for orders in one
  of these files:
   - models/semantic/sem_orders_docs.yml
  models/_semantic.yml

So the old sem_orders.yml pattern no longer works for semantic models. Keep one YAML file per mart, as in the layout above, and let it grow: a mart’s documentation, tests and semantics are reviewed together.

What can live elsewhere

Single-model metrics that only use one semantic model can stay under the model’s metrics: list.

Inside model-paths

Every file must sit under a folder listed in model-paths (usually models/). dbt’s guide warns that YAML outside it produces an empty semantic_manifest.json: no error, just no metrics.

Naming

Check the structure

dbt parse
mf validate-configs      # or, with dbt platform: dbt sl validate
mf list metrics

mf list metrics (or dbt sl list metrics) shows every metric the parse found, which is the quickest way to spot a file that was not picked up.

Tested with dbt Core 1.12 and MetricFlow 0.213 on DuckDB.

References