Model Governance¶
Group 1 - Part 2
mediapulse_base already uses one governance feature without you having touched it: every model in the project has an access modifier, set in dbt_project.yml. This section is about understanding what's already there, then extending it deliberately.
Exercise¶
Step 1 - Read the current access configuration¶
- Step complete
Open mediapulse_base/dbt_project.yml. Note the access setting for each layer: staging is public, intermediate is protected, and marts are protected by default except the streaming marts folder, which is overridden to public.
Hint: what each access level means
public models can be referenced from anywhere, including other dbt Mesh projects; protected (the default) can only be referenced within the same project; private restricts referencing to models in the same group. See Model access for the full behaviour of each level.
Step 2 - Explain the current choices¶
- Step complete
Why do you think staging models are public here, given mediapulse_analytics needs to ref() several of them? Why are the ads and podcasts marts left protected while streaming marts are public - what does that tell you about which marts are meant to be consumed outside this project?
Step 3 - Find and fix a real access mismatch¶
- Step complete
Open mediapulse_base/models/marts/ads/_marts__ads__models.yml and read dim_campaigns's own description closely against what you just learned about the ads marts folder's actual access level. There's a real contradiction between what the model's documentation promises and what its configuration currently allows. Fix it: add an access: public override for dim_campaigns specifically, without changing the access level of fct_ad_impressions (which has no such cross-project claim in its own docs and should stay protected).
Hint: per-model override vs folder-level override
A folder-level +access: change in dbt_project.yml would affect every model in that folder; since only one model in ads/ needs to change, a per-model config: access: public in dim_campaigns's YAML entry is the more targeted fix. See Model access for both config shapes.
Step 4 - Introduce a model group¶
- Step complete
Pick a small cluster of related models (e.g. the ads staging + marts models) and assign them to a group in dbt_project.yml. Set one model's access to private and confirm (by trying to reference it from outside the group) that dbt enforces the restriction.
Hint: groups and private access
A group gives a set of models a named owner, and private access restricts a model so only other models in the same group can reference it - useful for models that are genuinely internal implementation details, not stable interfaces. See About model governance for how groups and access levels combine.
Deliverable: your explanation of the current access choices, your fix for the dim_campaigns access mismatch, plus your new group definition and a screenshot/log showing the private restriction being enforced (or the compile error it produces).
Extension¶
Step 1 - Set up real groups for all four domains¶
- Step complete
Add a top-level groups: definition (in a .yml file under models/) with one group per domain - news, podcasts, streaming, ads - each with a name and an owner. Then assign every model in each domain to its group using the project-level +group: config in dbt_project.yml, replacing the single-cluster group you set up in the Exercise.
Hint: groups vs the access you already set
Defining a groups: block and assigning models to it is a separate step from setting access - a model can be public and still belong to a group. See Groups for the exact groups: and group config syntax, including the three places a model can be assigned to a group.
Step 2 - Enforce a contract on dim_campaigns¶
- Step complete
Now that dim_campaigns is public, lock its schema down: add config: contract: enforced: true to its YAML entry, and add a data_type to every one of its columns (all seven - dbt requires the full column list once a contract is enforced, not just the ones you want to protect). Run dbt build --select dim_campaigns to confirm it still builds cleanly against the contract.
Hint: what breaks if you get it wrong
Once enforced, a contract fails the build if the model's actual output doesn't match the declared column names, order, or data types exactly - deliberately break one data_type first (e.g. put the wrong type on budget_dollars) and run the build to see the real error before fixing it. See Model contracts for what's validated.
Step 3 - Decide whether a future change would need a model version¶
- Step complete
Suppose you needed to rename a column on this now-contracted, public model that mediapulse_analytics already depends on. Research what dbt model versions are for, and explain why "just rename the column" is the wrong move once a model has consumers outside your control.
Hint: versions exist for breaking changes
Model versions let multiple versions of a model coexist (e.g. dim_campaigns_v1, dim_campaigns_v2), with a latest_version marking the default and a defined_in config pointing each version at its file - giving consumers a migration window instead of forcing an immediate break. See Model versions for the exact config shape.
Deliverable: your groups: definition and per-model assignments, the enforced contract on dim_campaigns (including the build output from your deliberate break-then-fix check), and your written explanation of when you'd reach for a model version instead of editing in place.
Done?
You've fixed a real governance bug this project shipped with, organised its models into groups, and enforced a contract - the same tools mediapulse_analytics will lean on when it starts consuming this project properly.
Now head to Incremental modeling to stop a high-volume fact table from fully rebuilding every run.