Relational modeling is an important first step in preparing the transformations in dbt. Before the complex effort of writing SQL begins in dbt, the relational model helps engineers discover the data sources necessary to build the required transformation and serves as a high-level reference for pre-validation with business stakeholders.
Once the relational structure has been defined and validated, a dbt model contract can ensure that the dbt model logic that follows matches that expectation.
This article describes how to use dbt contracts to enforce consistency between the designs built in SqlDBM and the logic written in dbt.
What is a dbt model contract?
A dbt model contract is a model config property that enforces columns, data types, and constraints in the associated model. By assigning and enabling a model contract, dbt checks that the model being built conforms to the outlined specification. If the column list or data types don't match the contract, the build fails, highlighting the offending discrepancy.
An example of a dbt model contract
models:
- name: dim_customers
config:
contract:
enforced: true
columns:
- name: customer_id
data_type: int
constraints:
- type: not_null
- name: customer_name
data_type: string
...
Read more in dbt documentation.
dbt contracts - a sample workflow
In this example, we will run through a sample workflow that takes a transformational design from requirement to analysis to implementation—tying everything together using a contract.
In this scenario, a business stakeholder asks us to create a model that assigns loyalty points to customers based on their order volume.
Step 1 - self-service and discovery
To create the loyalty points model, we must understand where the required sources reside and how to link them. To do this, we review the relational model and, if necessary, confer with business stakeholders to locate the required sources.
This is done using the search and diagram navigation features in SqlDBM, allowing the user to locate the desired entities and review any related diagrams and documentation.
Step 2 - high-level design and validation
Once the data sources have been located, the engineer designs the object's structure to match the business requirements. If the design correctly matches business expectations, we can proceed to define the remaining dbt properties.
Step 3 - generating a model contract
Now that we have a structure in place for both Sources and the Model that we will be building in dbt, we add additional metadata (e.g., descriptions) and dbt properties (e.g., freshness, tests, model configs) to generate robust and meaningful YAML for handoff to the dbt project.
Adding descriptions and properties
It is important to ensure meaningful descriptions exist in the YAML artifacts generated for sources and models. Other dbt properties like tests, freshness, and configs should also be added here according to the requirements. This can be achieved from the Database documentation screen or Excel upload.
Remember to include the model contract in the model_config property by adding the following:
contract:
enforced: true
Generating YAML
Once descriptions and dbt properties have been added, proceed to the Forward engineer screen to generate the desired YAML files in Source or Model format.
Here, remember to enable the generation of data types and constraints used by the contract specification.
Please see the related article at the end of this page for more info on editing and generating dbt YAML.
Step 4 - enforcing a model contract in dbt
Proceed to the dbt project to create the loyalty points model and the required SQL logic based on business requirements. In parallel, create a corresponding model.yml
file using the YAML generated in the previous step.
Once the model logic has been written and tested, attempt to build the model in your development environment.
If the model does not match the contract, the build will fail, and you will have to fix any deviations after reviewing the related logs.
What is the difference between dbt tests and model contracts?
Tests and contracts are important tools in your data quality arsenal. This is why it's important to remember their fundamental difference: tests run after the model is built and on every subsequent run. Contracts are applied before a model is built and shape the way it is created, for example, applying column data types and not null constraints. Contracts allow you to catch errors and correctly shape the model in development, often eliminating the need for post-hoc tests, thus saving you time and money.
Once the discrepancies have been addressed and the build succeeds, you can deploy and run this model as part of your jobs in other environments—confident that the resulting object matches business expectations.
Conclusion
In this article, we learned about dbt model contracts and how they ensure consistency and data quality when building dbt models to meet business stakeholder requirements. A model contract helps unite the relational model with the resulting dbt transformation and ensures a perfect match between the two.
Using the sample workflow outlined in this guide, data teams can leverage the information in the relational model to accelerate the analysis, handoff, and testing required to build a dbt model.