データ契約(Data Contract)とは?ODCSとdbtでスキーマ破壊を止める実装を解説【2026年版】
データ契約は、データを出す側と使う側が、スキーマ・意味・品質・鮮度の約束を機械可読なファイルとして交わす仕組みです。上流の列名変更ひとつで下流の集計が静かに壊れる事故を、レビューとCIで検知できる形に置き換えます。この記事では、ODCS v3.1.0のYAMLが何を宣言するのか、dbtのcontract設定が何を必須にして何を止めるのか、制約の効き方がSnowflakeとPostgreSQLで割れる理由、互換性モードでデプロイ順序が入れ替わる仕組み、導入を見送るべき組織条件を扱います。
まとめ|データ契約の導入可否を分ける条件と最初に決める3項目
データ契約が投資に見合うのは、次の3つが揃うときです。上流の変更が下流を壊した実績が複数回あること。上流と下流のチームが別で、口頭調整では追いつかない規模であること。違反を検知したときにビルドやパイプラインを止める権限がデータ基盤側にあること。3つ目が無い組織では、契約ファイルだけが増えて誰も守らない状態になります。
最初に決めるのは、契約を置く層・記述する規格・強制する場所の3つです。層は下流が直接参照する公開テーブルに限り、生の取り込みテーブルには張りません。規格は組織横断で配るならODCS、単一のdbtプロジェクト内で完結するなら dbt の contract 設定だけで足ります。強制はモデルのビルド前に置くのが最も安く済みます。
見送ってよい条件も裏返しに決まります。生産者と消費者が同じチームで、テーブル数が十数本の規模なら、記述と維持のコストが事故の期待損失を上回ります。代替策と形骸化の兆候は最終章に示しました。
生産者と消費者の合意をコード化するデータ契約の定義と担当範囲
データ契約は「このテーブルはこの形で、この頻度で、この品質で提供する」という約束を、口約束ではなくバージョン管理されたファイルとして持つ考え方です。契約という語は法務文書ではなく、APIのインターフェース定義に近い意味で使われます。
スキーマ・品質・所有者・SLAを1枚のYAMLに束ねる構成要素
典型的なデータ契約が抱える情報は、次の5系統です。どれか1つでも欠けると、違反したときに誰へ何を言えばよいのかが決まりません。
- 構造:列名・データ型・NULL可否・主キー
- 意味:各列が業務上何を指すかと、値の取りうる範囲
- 品質:欠損率・重複率・件数の下限など検証可能なルール
- 提供条件:更新頻度・遅延の上限・保持期間・可用性
- 責任:所有チーム・問い合わせ先・権限申請の窓口
実務でまず埋まらないのは意味と責任です。構造と品質はテーブル定義から機械的に起こせますが、この列が何を数えた値なのかは生産者しか知りません。契約の価値の大半はここにあります。
データ品質テストやスキーマレジストリと役割が重なる部分の切り分け
機能が重なる仕組みが2つあります。ひとつはデータ品質テストで、実データが期待どおりかを事後に検証するもの。契約は何を提供すると約束したかを事前に宣言する側なので、品質テストは契約の一部を実行可能にした下位機能にあたります。契約から検証コードを生成する流れが自然です。
もうひとつがスキーマレジストリで、Avroなどのスキーマを中央登録して互換性を審査する仕組みです。扱う範囲は構造に限られ、所有者・SLA・意味づけは持ちません。ストリーミング前提ならレジストリを構造の実行装置として使う分担になります。
データメッシュのプロダクト思考からデータ契約が要請された経緯
広まった直接のきっかけは、データを中央集権的なチームが面倒を見る体制から、生成したドメインが責任を持つ体制へ移す動きでした。データメッシュの設計原則では、各ドメインが自分のデータをプロダクトとして提供します。プロダクトである以上、利用者への仕様提示が要る。その仕様書が契約です。
ODCSの原型も、PayPalがデータメッシュ戦略の一環で社内に定着させた運用を外部へ公開したものでした。組織を分割した結果として調整コストが跳ね上がり、その調整を文書とCIに肩代わりさせる必要が生じた、という順番です。組織が分かれていないのに契約だけ入れると、コストだけが残ります。
ODCS v3.1.0が定めるデータ契約YAMLの構造と記述の粒度
ODCSはLinux Foundation AI & Data のBitolプロジェクトが管理する規格です。2023年11月30日に発足し、59本のRFCを経て同財団から満場一致で卒業しました。最新版は2025年12月アナウンスの v3.1.0、ライセンスはApache 2.0です。
apiVersionとkindとstatusで契約の版と成熟度を固定する記法
ODCSの契約は単一のYAMLファイルで、フル例の最上位キーは domain、dataProduct、version、status、id、description、tenant、kind、apiVersion、servers、schema、price、team、roles、slaProperties、support、tags、customProperties の順に並びます。v3.1.0 の契約では apiVersion に v3.1.0 を、kind に DataContract を置きます。
混同しやすいのが version と apiVersion の違いです。前者は契約そのものの版で、列を追加すれば上がります。後者は規格の版で、パーサが読めるかを決めるもの。status には draft や active といった成熟度を置き、下流は active 以外を参照しないという運用ルールを添えて初めて意味を持ちます。
schemaセクションで物理名と論理名と型を宣言するときの粒度
schema セクションには、テーブルに相当するオブジェクトと配下のプロパティ(列)を並べます。物理名と論理名を別に持てるのが特徴で、業務側が呼ぶ名称とデータ基盤側の実体名がずれていても翻訳表を内包できるつくりです。三層スキーマにおける外部スキーマと概念スキーマの分離をファイルに落とした形にあたります。
迷うのは、どこまで列を書くかです。全列を書けば維持コストが上がり、主要列だけなら書かれていない列を下流が勝手に使い始めます。参照してよい列だけを載せ、契約に無い列は予告なく消えると明示する運用が扱いやすくなります。
slaPropertiesとqualityで鮮度と件数と欠損を数値化する記述
slaProperties には、鮮度(対象時刻からどれだけ遅れて届くか)、更新頻度、保持期間、可用性を単位付きの数値で置きます。「毎日更新」ではなく「日次、対象日の翌日9時までに、遅延上限3時間」と書けるかどうかで、違反判定を自動化できるかが決まる部分です。指標の立て方はSLOとSLAの設定方法と同じ考え方が使えます。
品質のルールは、テキストによる説明、ライブラリ提供のルール、SQL式、カスタム実装のいずれかで宣言する形式。SQL式にしておけば Data Contract CLI が実データへの検証クエリへ変換でき、契約と検証コードの二重管理が消えます。文章だけで書くと、契約はレビュー用の飾りに留まります。
teamとrolesで所有者と権限申請先を契約側に固定する意味
team には所有チームと担当者、roles にはアクセス権限と申請先、support には問い合わせチャンネルのURLを書きます。地味ですが運用が始まると最も参照される箇所で、品質異常を検知した深夜に誰を叩き起こすのかが契約ファイルにある状態と、社内Wikiのどこかにあるはずの状態では復旧までの時間が変わります。
dbtのcontract設定でスキーマ違反をビルド失敗に変える実装
契約を宣言しただけでは何も止まりません。dbtを使っている組織では、モデル契約が最も導入コストの低い強制点になります。
enforced trueが全列のdata_type宣言を必須にする前提条件
dbtのモデル契約は dbt Core v1.5(2023年4月)で導入されました。モデルの設定に contract を置き、その下の enforced を true にすると有効になります。有効化した瞬間から、そのモデルの全列について name と data_type の定義が必須になります。1列でも欠けているとビルドは通りません。
この「全列必須」が導入の実質的なハードルです。50列のワイドテーブルなら50行の型定義を書き、以後の変更のたびに保守することになります。だからこそ張る対象を絞る必要があり、dbtの三層構造でmarts層が最終整備を担う原則に従って下流が直接参照する層だけに限定する判断が効いてきます。
実行前チェックが列名と型の不一致をビルド開始前に止める挙動と例外
契約付きモデルをビルドすると、dbtはクエリが返す列と契約の宣言を突き合わせる実行前チェックを走らせます。比較は列の順序に依存せず、名前と型だけを見る方式。不一致があればモデルを実行せずに失敗するため、壊れたテーブルが下流へ出ていく前に止まります。
対応するマテリアライゼーションには例外があります。table は全機能が使え、view は名前と型のみの検証で制約は対象外、incremental は on_schema_change が append_new_columns か fail のときに限られます。Pythonモデル、materialized view、ephemeral、カスタムマテリアライゼーション、再帰CTEを含むBigQueryモデル、sources・seeds・snapshots は非対応。契約を張りたい箇所が全部対象外だった、という事態を避けるため先に確認してください。
constraintsの強制度がデータ基盤ごとに割れる実装差の一覧
契約の一部として扱われるのは「強制される制約」だけです。同じ記述でも、基盤によって本当に効くか、DDLに書かれるだけかが変わります。
| 制約 | Snowflake | BigQuery | PostgreSQL | Databricks |
|---|---|---|---|---|
| not_null | 強制される | 強制される | 強制される | 強制される |
| primary_key | 定義のみ | 定義のみ | 強制される | 定義のみ |
| foreign_key | 定義のみ | 定義のみ | 強制される | 定義のみ |
| unique | 定義のみ | 非対応 | 強制される | 非対応 |
| check | 非対応 | 非対応 | 強制される | 定義のみ |
Snowflakeで主キーの一意性が保証されると思い込むと、重複行が下流へ流れます。Sparkの not_null はビルド後の検査、Athenaは制約そのものが非対応です。分析基盤がSnowflakeやBigQueryなら、一意性と参照整合性はdbtのテストや品質ルール側で担保する前提で設計してください。
model versionsとdeprecation_dateで移行期間を確保する手順
契約を守るとは、変更しないことではなく、変更の告知手順を決めることです。dbtにはモデルのバージョン管理機能があり、旧版と新版を並走させたうえで旧版に廃止予定日を設定できます。
- 新しい形の版を追加し、旧版と並行して提供する
- 旧版に廃止予定日を設定し、下流に警告を出す
- 移行期間の経過後に旧版を削除する
破壊的変更として検知されるのは、既存列の削除、列の型変更、制約の削除・変更、契約付きモデル自体の削除・改名・無効化です。バージョン付きモデルではエラー、バージョンなしでは警告。移行期間を持ちたいなら最初からバージョン付きで設計してください。
互換性モードの選択で決まる破壊的変更の範囲とデプロイ順序の判断
ストリーミングを含む構成では、Schema Registryの互換性モードが契約の実行装置になります。選んだモードで、許される変更と、生産者と消費者のどちらを先にデプロイするかが決まります。
BACKWARDとFORWARDで入れ替わる消費者と生産者の更新順序
Confluent Schema Registryの互換性タイプは7種、既定はBACKWARDです。名前が示すのは読み取り可否よりも更新順序そのものだと捉えると混乱しません。
| モード | 許される変更 | 先に更新する側 |
|---|---|---|
| BACKWARD(既定) | 任意列の追加、列の削除 | 消費者 |
| FORWARD | 列の追加、任意列の削除 | 生産者 |
| FULL | 任意列の追加と削除のみ | どちらでもよい |
| NONE | すべて(検査なし) | 同時に全部 |
消費者が多数いて個別に更新できない構成ではBACKWARDが扱いにくく、生産者を先に動かせないならFORWARDを選びます。両方を独立に動かしたいならFULLですが、許される変更が任意列の増減だけに狭まります。なお、Kafka StreamsはBACKWARDのみ対応です。状態ストアや変更ログの過去データも読むためで、選択の余地はありません。
TRANSITIVE系を選ぶ条件と過去全版を照合する運用上の代償
BACKWARD、FORWARD、FULLにはそれぞれTRANSITIVE付きの版があり、通常版が直前のバージョンとだけ照合するのに対し、TRANSITIVE付きは過去の全バージョンと照合します。選ぶ条件ははっきりしていて、古いデータを再処理する運用があるならTRANSITIVE付きです。通常版では、v1で書かれた過去ログをv3のコンシューマが読めなくなる事態が起こりえます。代償は変更の自由度で、一度削除した列名を型を変えて再利用する操作が弾かれます。イベントを短期間で捨てる構成なら通常版で十分です。
列削除と型変更が破壊的変更に該当する境界と非破壊で逃がす手筋
破壊的変更を避ける手筋は限られています。列名を変えたいときは、新しい名前の列を追加して両方を書き、下流の移行を確認してから旧列を削除する二段構え。型を広げたいとき(整数から浮動小数点など)も、直接の型変更ではなく別列の追加で逃がすのが安全です。
ここで判断を言い切ります。「下流が使っていないはずだから」という推測を根拠に列を消してはいけません。その推測を消すことが契約の目的だからです。確証が取れないなら廃止予定日を設定し、期間を置いてから削除します。確証を取る手段が無い組織では、契約よりリネージの可視化を先に入れるべきです。
契約ファイルの置き場所とCI検査でデータ契約を運用に乗せる設計
契約は書いた瞬間から陳腐化を始めます。実データとの一致を機械が毎日確かめる経路が無ければ、半年後には誰も信じない文書になります。
契約YAMLの配置先とレビュー経路を生産者側に寄せる構成の理由
契約ファイルは、生産者のリポジトリに置きます。データ基盤チームのリポジトリにまとめる構成は一見管理しやすく見えますが、生産者がレビュー中に契約の差分を目にしない状態を作る配置です。アプリケーションのスキーマ変更と契約の変更が同じプルリクエストに現れる形が、事故を最も減らします。
下流からの参照は、生産者リポジトリからパッケージとして配布する形にします。コピーを配ると、どれが正なのか分からなくなるためです。契約の所在をカタログ側で引けるようにしておくと、消費者が探す手間も消えます。
datacontract CLIのlintとtestをCIのどの段に置くかの判断
Data Contract CLI はPython製で、init、lint、test、export、import、dbt、catalog、publish、api のコマンドを備えます。ODCSをネイティブに扱い、Avro、dbt models、JSON Schema、SQL DDL、SodaCL、Protobuf など25以上の形式へ変換可能。接続先はSnowflake、BigQuery、Kafkaなど18以上です。
置き場所は2段に分けます。lint は契約YAMLの構文と構造だけを見るためデータ接続が不要で、プルリクエストのたびに走らせても数秒で終わる軽さ。test は実データへ接続するのでコストと実行時間がかかります。前者をPRのチェックに、後者を日次ジョブに置く分担が扱いやすい構成です。
違反検知でパイプラインを停止する層と通過させる層を分ける設計
すべての違反で止めると運用が回りません。層で分けます。取り込み層は止めずに記録だけ残し、生データはそのまま保管する扱い。ここで止めると、上流の一時的な異常でデータの欠落が確定します。
止めるのは、下流が直接参照する公開層への書き込み前です。ここでの違反は間違った数字が業務判断に使われることを意味するため、古いデータを残したまま更新を止めるほうが被害は小さくなります。中間層は構造の違反を停止、品質しきい値の逸脱を警告に留める配分が現実的。この設計を先に決めておかないと、深夜に鳴ったアラートを誰も判断できません。
データ契約を採用すべき条件と導入を見送るべき組織状況の見極め
仕組みとして正しくても、組織の状態によっては維持コストだけが残ります。導入の可否を条件付きで示します。
投資に見合う3条件と最初に契約化するテーブルを絞り込む判断基準
冒頭に挙げた3条件は、いずれも記録で確かめられます。破損の実績はインシデント記録の件数、チーム分離は変更の事前共有が仕組みで担保されているか、停止権限は過去に実際にパイプラインを止めた事例があるかで判定してください。3つ目が無いまま導入すると、警告だけが積み上がります。
最初に契約化する対象は、下流の参照数と破損時の影響で絞ります。経営指標のダッシュボードが参照するテーブル、外部へ提供しているデータ、機械学習の特徴量を作る入力の3つが典型。取り込んだままの生テーブルには張りません。上流の都合で変わる前提の層に張ると、更新作業だけが増えます。
導入を見送るべき組織条件と代わりに置く軽量な代替策の組み立て
基盤の構築自体が進行中で、テーブル定義が週単位で変わる段階でも契約は早すぎます。この時期に張ると、書き換えが開発の足かせになるためです。
代替策は3つの順で軽くなります。dbtのテストで not_null と unique と参照整合性だけを張る、スキーマ変更を検知して通知するジョブを日次で回す、主要テーブルの列定義をレビュー必須のファイルとして1箇所に置く。まずは2番目から始め、通知が事故を捕まえた回数を数えると、契約へ進むべきかが数字で判断できます。基盤の設計から固めたい場合は、データ分析基盤構築・MLOps構築支援のような外部の設計支援を挟むと、契約を張る層の切り方まで整理できます。
契約が実データと乖離して形骸化する失敗パターンと早期の検知策
最も多い失敗は、導入から3か月ほどで実データとの乖離が始まるパターンです。原因は決まっていて、契約の更新が任意の作業になっているため。契約を変えなくてもテーブルが変えられる経路が1本でも残っていると、そこから崩れます。
検知は単純です。実データの検証を日次で回し、長期間検証が走っていない契約を洗い出します。それは参照されていないか、検証ジョブが壊れているかのどちらか。もうひとつの兆候が、契約のバージョンが半年以上上がらないのにテーブルの列数が増えている状態で、契約を通さない変更経路が生きている証拠です。四半期に一度この2点を確認する運用を、導入と同時に決めてください。
よくある質問
データ契約の導入時に実装者から挙がることの多い質問をまとめました。
データ契約とデータ品質テストは何が違いますか?
データ品質テストは、実データが期待どおりかを事後に検証する仕組みです。契約はこれから何をどの品質で提供するかを事前に宣言する合意で、品質ルールはその一部にすぎません。所有者・問い合わせ先・更新頻度・保持期間といった提供条件も入るぶん、範囲は広くなります。
データ契約は小規模なチームでも必要ですか?
生産者と消費者が同じチームで、対象テーブルが十数本の規模なら不要です。判断の目安は、上流の変更による下流の破損が過去半年で複数回起きているかどうか。起きていないなら、スキーマ変更を検知して通知するジョブを日次で回すところから始めてください。
ODCSとdbtのcontract設定はどちらを選べばよいですか?
目的が違うので排他ではありません。dbtの contract 設定は、単一プロジェクト内で列名と型の不一致をビルド前に止める強制装置。ODCSは、所有者・SLA・品質ルール・意味づけまで含めた合意を組織をまたいで配布する記述規格です。社外や他部門へ提供するならODCSで配り、強制はdbt側に任せます。
データ契約に違反したらパイプラインは止めるべきですか?
層によって分けます。取り込み層は止めずに記録だけ残してください。止めるべきは、下流が直接参照する公開層への書き込み前です。間違った数字が業務判断に使われるより、古いデータを残して更新を止めるほうが被害は小さく済みます。
データ契約の導入はどのテーブルから始めればよいですか?
下流の参照数と破損時の影響が大きいテーブルからです。経営指標のダッシュボード、外部提供データ、機械学習の特徴量を作る入力の3つが典型的な出発点になります。dbtを使っているなら、下流が直接参照する最終整備層に限って有効化する切り方が扱いやすくなります。
関連記事
- データカタログとは?意味・メタデータ管理の仕組みからAI時代の導入判断まで解説:契約の所在と意味づけを消費者が探せるようにする仕組みです。
- データパイプラインとは?種類・構成要素と冪等性から考える設計判断を解説:契約違反で停止した処理を安全に再実行するための前提を扱います。
- Databricks DQXとは?PySparkデータ品質検証フレームワークの機能と使い方:契約の品質ルールを実データへ当てる検証側の実装例です。
- リバースETLとは?同期モードと差分検出の仕組み・API制約から実装判断を解説:契約を守ったデータを業務SaaSへ書き戻す下流工程にあたります。