dbt はソフトウェア工学の原則をデータ変換に適用できるのが強みです。特に、契約(schema を固定)、所有と境界(groups + access)、互換性の進化(versions)は、ガバナンスの要になります。
本稿では、各機能の役割と相互作用、よくある設計パターン、移行時の注意点を、公式ドキュメントに基づく安定概念に絞って解説します。
データ製品を継続的に改良しつつ、下流の安定性を損なわないためには、インターフェースの固定、チーム境界の明確化、破壊的変更の管理が必要です。dbt では、contracts(モデルの入出力スキーマを固定)、groups + access(所有と参照の境界を宣言)、versions(互換性を意識した進化)でこれを表現します。
これらはビルドの成否や参照可否に直接影響し、リグレッションを未然に防ぎます。試験観点では、用語の正確な使い分けと、いつどの設定を使うべきかの判断が狙われます。
| 機能 | 主目的 | 宣言場所(代表例) | 破壊的変更の扱い |
|---|---|---|---|
| contracts | モデル出力のスキーマ固定 | model の config(schema.yml で columns と型) | 互換性を壊す変更は基本 NG。必要なら新 version を作る |
| groups + access | 所有境界と参照ポリシーの宣言 | groups リソース定義 + 各 model の group/access | private なら外部参照不可。public は参照可。protected は限定公開 |
| versions | 互換性の進化と並行稼働 | model の versions 定義 + latest_version | 破壊的変更は新しい v を追加し段階的移行 |
最小構成の全体像(骨子)
# プロジェクトの骨子(抜粋)
# models/schema.yml などに集約する設計が読みやすい
models:
- name: customers
group: mart
access: public
config:
contract:
enforced: true
columns:
- name: customer_id
data_type: string
- name: country
data_type: string
versions:
- v: 1
defined_in: models/marts/customers_v1.sql
- v: 2
defined_in: models/marts/customers_v2.sql
latest_version: 2
groups:
- name: mart
owner:
name: Analytics Mart
email: [email protected]
contracts はモデル出力の列名とデータ型を固定する仕組みです。config の contract.enforced を true にし、columns に name と data_type を宣言します。定義に合わない SELECT を書くと、ビルド時にエラーになります(アダプタにより動作は異なりますが、逸脱は少なくとも dbt 実行時に検出されます)。
実務では、信頼できる下流インターフェースを作るファサード層(例えば mart 層)に適用します。破壊的変更(列削除・型変更)が必要な場合は versions を併用して新バージョンを提供するのが安全です。
contracts の最小例(schema.yml)
models:
- name: orders
group: mart
access: public
config:
materialized: table
contract:
enforced: true
columns:
- name: order_id
data_type: string
- name: order_date
data_type: date
- name: amount
data_type: numeric
# models/marts/orders.sql
-- SELECT の列と型が上記と一致しないとエラー
select
cast(order_id as string) as order_id,
cast(order_date as date) as order_date,
cast(amount as numeric) as amount
from {{ ref('stg_orders') }}
groups はリソースの所有グループを宣言する仕組みで、owner 情報と合わせて定義します。各モデルに group を設定し、access で公開レベル(private / protected / public)を指定します。これにより、異なるグループからの参照可否がコンパイル段階でチェックされます。
実装はシンプルでも効果は大きく、プロジェクトを跨ぐ“無秩序な参照”を抑止できます。公開が必要なモデルは public、内部専用は private を基本方針にし、protected は段階的公開や合意の上での限定利用に用いるのが無難です。
groups と access の定義例
# groups.yml
groups:
- name: staging
owner:
name: Data Platform
email: [email protected]
- name: finance
owner:
name: Finance Analytics
email: [email protected]
# models/finance/schema.yml
models:
- name: fct_revenue
group: finance
access: private # finance 外からの ref を禁止
config:
contract:
enforced: true
columns:
- name: revenue
data_type: numeric
# 参照側での選択(CI 等)
# finance グループだけをビルド
# dbt build --select group:finance
versions はモデルをバージョン単位で並行提供するための仕組みです。versions に v を列挙し、各バージョンの SQL を defined_in で紐付け、latest_version を指定します。ref('model') は既定で latest に解決され、ref('model', version=1) のように固定もできます。
列の削除や型変更といった破壊的変更は新しい v を追加します。旧版は一定期間残し、下流の移行完了後に廃止します。contracts と組み合わせると、各バージョンのインターフェースを明確に保てます。
versioned model の定義と参照例
# models/mart/schema.yml
models:
- name: customers
group: mart
access: public
config:
contract:
enforced: true
versions:
- v: 1
defined_in: models/marts/customers_v1.sql
- v: 2
defined_in: models/marts/customers_v2.sql
latest_version: 2
columns:
- name: customer_id
data_type: string
# 参照側
-- latest を利用
select * from {{ ref('customers') }}
-- v1 を明示的に利用
select * from {{ ref('customers', version=1) }}
運用では、公開インターフェース(public)のみ contract.enforced と versioning を徹底し、private な内部モデルは簡素に保つのが現実的です。グループ境界での参照は、原則として public モデルに限定します。
移行は「新 v の追加 → 下流の段階的移行(必要なら一時的に v1/v2 のブリッジを提供) → latest 切替 → 旧 v 廃止」の順で進めます。CI では group 単位と公開モデル単位のテストを分離すると、失敗範囲が特定しやすくなります。
設計の流れ(公開モデルの進化)
移行期の CI セレクタ例
# 旧版と新版を並行検証(公開モデルのみ)
# 新版を優先して build し、旧版は smoke テストに限定
# 最新版のグループ単位で失敗を早期検知
dbt build --select access:public +state:modified --exclude group:archive
dbt test --select model:customers,version:1 --store-failures
試験では、用語の対応付け(contract はスキーマ固定、group/access は参照境界、version は破壊的変更の吸収)と、どの状況でどれを使うかの判断が頻出です。具体的な YAML の書き方、ref の解決規則、エラーが出るタイミングを押さえてください。
実務では、公開面だけ厳格化する“選択と集中”が有効です。まずは public モデルの contracts と versions を整備し、groups と access で境界を固定します。移行ルールと連絡経路(owner)を明文化すれば、変更の社会コストが下がります。
セルフチェック用コマンド断片
# 公開モデルの契約とテストの健全性
/dbt build --select access:public
# 変更影響の把握(state 比較)
dbt ls --select state:modified+ --defer --state target/production
# グループ所有者の棚卸し(docs 生成と合わせて)
dbt docs generate && dbt docs serve
Analytics Engineer
問題 1
mart グループが提供する public モデル customers に対し、下流(別グループ)が参照している列 country を削除したい。最も適切な対応はどれか?
正解: A
列削除は破壊的変更の典型。versioned model で新 v を作り、contracts を更新、latest 切替と並行提供期間を設けて段階的に移行するのが安全で、dbt の推奨設計に沿います。
contracts はどのアダプタでも同じように強制されますか?
列名・型の不一致は少なくとも dbt 実行時に検出されますが、データベース制約の作成や動作はアダプタ依存です。Snowflake や BigQuery など主要アダプタで安定的に利用されています。実環境で一度ビルドし、期待どおりに失敗することを確認すると安心です。
groups と Git のチームや権限は連動しますか?
groups は dbt プロジェクト内のメタデータで、Git の権限とは直接連動しません。owner 情報(名前・メール等)を groups に記述し、変更フローやレビュー体制はリポジトリ側の運用(CODEOWNERS など)で補完します。
versions はすべてのモデルで必須ですか?
必須ではありません。グループ外から参照される public モデルや、長寿命の下流依存があるモデルでの適用が効果的です。private な内部モデルは、コストとのバランスを見て必要時のみ導入します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
dbt Model の基礎: SQL で定義する変換の最小単位
Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...
dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点
dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...
dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点
dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...
dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト
Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....
dbt_project.yml の読み方:主要設定と命名を最短で掴む
dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...