※ ヤプリ&フラー 合同アドベントカレンダー Advent Calendar 2025(1枚目) の12日目にもクロスエントリーしています。
こんにちは!データサイエンス室(以下、DS室)の山本です(@__Y4M4MOTO__)です。
先日、dbtプロジェクトの models/ ディレクトリ構造を見直し、再構築を実施しました。この記事では、 なぜ再構築するに至ったか という課題と、 再構築前後のディレクトリ構造の違い について紹介します。
models/ ディレクトリの構造に悩んでいる方や、dbtプロジェクトの整理を検討している方の参考になれば幸いです🙇
従来のディレクトリ構造とその課題
従来のディレクトリ構造
従来のディレクトリ構造と各ディレクトリの役割を以下に示します。
models/ ├── source/ ... Sourceの定義 │ ├── staging/ ... Staging層。Viewでmaterializedし、カラム名の是正や型変換などを行う │ ├── component/ ... Intermediate層(+ Mart層。詳細は後述)。 │ 重複削除などの前処理を行い、 │ 汎用データマート(ディメンションテーブル、ファクトテーブル)を作成する │ ├── aggregation/ ... Mart層。ファクトテーブルを日単位で集計したテーブルを作成する │ ├── test_model/ ... dbt test用のdbtモデルを格納する │ ├── yappli_analytics/ ... Mart層。Yappli Analyticsダッシュボード用のデータマートを作成する │ ├── yappli_cms/ ... Mart層。 │ Yappliの管理画面に表示するダッシュボード(CMSレポート)のためのデータマートを作成する │ └── yappli_data_hub/ ... Mart層。Yappli Data Hubサービス用のデータマートを作成する
基本的な設計思想として、dbtの公式ドキュメントで推奨されている「Staging」「Intermediate」「Mart」の3層構造を採用していました。
最大の課題: component/ ディレクトリの役割の曖昧さ
最大の課題は、 component/ ディレクトリにIntermediate層とMart層のdbtモデルが混在し、その 役割が不明瞭になっていた 点です。
例えば、以下のデータリネージ図のように、汎用的なMart層のテーブル ( fct_table ) と、そのための前処理的な中間テーブル ( fct_table_A , fct_table_B ) が同じく component/ ディレクトリに格納されていました。
この混在により、次のような問題が発生していました。
- 分析者が混乱する : 事前知識がないと、Mart層として利用すべき最終テーブルがどれか分かりづらい。
- AIによるクエリ生成の精度低下 : AIにクエリを書かせる際、探索範囲が広がってしまい適切なテーブルを選択しづらくなる。
分析者が混乱するだけだったら正直「分かりづらいけどまあいいか…」で済ませてしまっていたのですが、AIにクエリを書かせるようになってからは 適切なクエリが生成されにくくなる という実害が出てきたため、対応の温度感が一気に上がりました。
その他の課題:3層構造と実際のディレクトリ構造の乖離
前述の通り、設計思想としては3層構造を採用していたものの、エディタでdbtプロジェクトを開くとファイルツリーには以下のように8つのディレクトリがアルファベット順で並列に並んでいるだけであり、 3層構造との対応がわかりづらい という課題もありました。
models/ ├── aggregation/ ... Mart層 ├── component/ ... Intermediate層 + Mart層 ├── source/ ... Source定義 ├── staging/ ... Staging層 ├── test_model/ ... dbt test用 ├── yappli_analytics/ ... Mart層 ├── yappli_cms/ ... Mart層 └── yappli_data_hub/ ... Mart層
再構築後のディレクトリ構造
課題解決のため、ディレクトリ構造を以下のように再構築しました(新規追加したディレクトリには✅マークを付けています)。
models/ ├── 0_source/ ... Sourceの定義 │ ├── 1_staging/ ... Staging層 │ ├── 2_intermediate/ ... ✅ Intermediate層。重複削除などの前処理を行う。 │ ├── 3_dwh/ ... ✅ Mart層。汎用データマート(ディメンション/ファクト)を作成する。 | ├── component/ ... 汎用データマートの作成 | └── aggregation/ ... 集計テーブルの作成 │ ├── 4_product_mart/ ... ✅ プロダクト用データマートを格納 | ├── yappli_analytics/ | ├── yappli_cms/ | └── yappli_data_hub/ │ └── test_model/ ... dbt test用のモデル
主な変更点と効果は以下の通りです。
Intermediate層とMart層の明確な分離
従来の component/ ディレクトリを、 2_intermediate/ と 3_dwh/ に分割しました。
これにより、Intermediate層とMart層のdbtモデルが明確に分離されました。分析者が利用すべきテーブルは 3_dwh/ 配下のみとなるため、迷いにくくなりました。
また、AIにクエリを書かせる際も、利用すべきテーブルの探索範囲が絞られるため、より適切なクエリが生成されやすくなりました。
3層構造に合わせたディレクトリのナンバリング
ディレクトリ名の先頭に 0_ 、 1_ 、 2_ 、 3_ 、 4_ と番号を振りました。
これにより、3層構造の順番(Source → Staging → Intermediate → Mart)とディレクトリの物理的な並び順が一致するようになり、3層構造との対応がわかりやすくなりました。
また、今後層内でさらに細分化が必要になった際も配下でサブディレクトリを切ればよいため、対応のわかりやすさを維持したまま柔軟に対応できます。
移行計画と実施
ディレクトリ構造の変更に伴い、dbtを実行しているワークフロー内で、各ジョブが実行対象のdbtモデルを指定しているパスも修正する必要がありました。
幸い、ジョブの実行順がおおよそ以下の2ステップとシンプルだったため、各ジョブのパスを差し替えるだけで対応できました。
- 汎用データマート作成ジョブの実行
- 各プロダクト用データマート作成ジョブの実行
パスの差し替えは、コマンドの差し替えは、 dbt ls を用いて新旧コマンドの実行対象モデルに過不足がないことを確認しながら進めました。
まとめ
この記事では、dbtプロジェクトの models/ ディレクトリ構造を再構築した経緯と内容について説明しました。
再構築の結果、Intermediate層とMart層のdbtモデルが明確に分離され、役割がわかりやすくなるとともに、ナンバリングによる認知負荷の軽減も実現できました。
正直、AIにクエリを書かせる世界線が来てなければもっと後に取り組んでいた内容だったと思います。AIにうまく働いてもらうために「名は体を表す」状態を作っておくことの重要性を実感しました。
ここまでお読みいただきありがとうございました!