Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

dbtプロジェクトのmodelsのディレクトリ構造を再構築してみた

この記事は dbt Advent Calendar 2025 の12日目の記事です。

ヤプリ&フラー 合同アドベントカレンダー 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層構造を採用していました。

docs.getdbt.com

最大の課題: component/ ディレクトリの役割の曖昧さ

最大の課題は、 component/ ディレクトリにIntermediate層とMart層のdbtモデルが混在し、その 役割が不明瞭になっていた 点です。

例えば、以下のデータリネージ図のように、汎用的なMart層のテーブル ( fct_table ) と、そのための前処理的な中間テーブル ( fct_table_A , fct_table_B ) が同じく component/ ディレクトリに格納されていました。

graph LR subgraph source/ source_table_A source_table_B end subgraph staging/ stg_table_A stg_table_B end subgraph component/ fct_table_A[ fct_table_A (table_Aのファクトテーブル) ] fct_table_B[ fct_table_B (table_Bのファクトテーブル) ] fct_table[ fct_table (汎用データマートのファクトテーブル) ] end source_table_A --> stg_table_A --> fct_table_A --> fct_table source_table_B --> stg_table_B --> fct_table_B --> fct_table

この混在により、次のような問題が発生していました。

  • 分析者が混乱する : 事前知識がないと、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にクエリを書かせる際も、利用すべきテーブルの探索範囲が絞られるため、より適切なクエリが生成されやすくなりました。

ディレクトリの分け方や命名などは、以下の記事を参考にさせていただきました。非常に参考になる内容なので、ぜひご一読ください。 zenn.dev

3層構造に合わせたディレクトリのナンバリング

ディレクトリ名の先頭に 0_1_2_3_4_ と番号を振りました。

これにより、3層構造の順番(Source → Staging → Intermediate → Mart)とディレクトリの物理的な並び順が一致するようになり、3層構造との対応がわかりやすくなりました。

また、今後層内でさらに細分化が必要になった際も配下でサブディレクトリを切ればよいため、対応のわかりやすさを維持したまま柔軟に対応できます。

移行計画と実施

ディレクトリ構造の変更に伴い、dbtを実行しているワークフロー内で、各ジョブが実行対象のdbtモデルを指定しているパスも修正する必要がありました。

幸い、ジョブの実行順がおおよそ以下の2ステップとシンプルだったため、各ジョブのパスを差し替えるだけで対応できました。

  1. 汎用データマート作成ジョブの実行
  2. 各プロダクト用データマート作成ジョブの実行
graph LR common[ 汎用データマート作成ジョブ (Staging層、Intermediate層、Mart層の汎用データマートを作成) ] cms[ CMSレポート用データマート作成ジョブ (Mart層のCMS用データマートを作成) ] ya[ Yappli Analytics用データマート作成ジョブ (Mart層のYappli Analytics用データマートを作成) ] ydatahub[ Yappli Data Hub用データマート作成ジョブ (Mart層のYappli Data Hub用データマートを作成) ] common --> cms common --> ya common --> ydatahub

パスの差し替えは、コマンドの差し替えは、 dbt ls を用いて新旧コマンドの実行対象モデルに過不足がないことを確認しながら進めました。

まとめ

この記事では、dbtプロジェクトの models/ ディレクトリ構造を再構築した経緯と内容について説明しました。

再構築の結果、Intermediate層とMart層のdbtモデルが明確に分離され、役割がわかりやすくなるとともに、ナンバリングによる認知負荷の軽減も実現できました。

正直、AIにクエリを書かせる世界線が来てなければもっと後に取り組んでいた内容だったと思います。AIにうまく働いてもらうために「名は体を表す」状態を作っておくことの重要性を実感しました。

ここまでお読みいただきありがとうございました!