LESSON 30分

ストーリー

田中VPoE
データレイクの最大の課題を知っているか?
あなた
「データスワンプ(データの沼)」ですよね。何でもかんでもデータレイクに投入した結果、何がどこにあるかわからなくなる
田中VPoE
その通り。Medallionアーキテクチャは、データを品質レベルに応じてBronze、Silver、Goldの3層に分けることで、この問題を解決する。Databricksが提唱したパターンで、レイクハウスの標準的な設計手法になりつつある
あなた
金属の品質になぞらえているんですね。Bronze(銅)が生データ、Gold(金)がビジネスで使える高品質データ
田中VPoE
そうだ。各層の責務を明確にすることで、データの信頼性とトレーサビリティを確保する。データエンジニアにとっての「Clean Architecture」だと思ってくれ

Medallionアーキテクチャの3層

全体構造

Medallion Architecture:

┌──────────────────────────────────────────────────────────┐
│                                                          │
│  ┌────────────┐    ┌────────────┐    ┌────────────┐     │
│  │   Bronze    │    │   Silver   │    │    Gold    │     │
│  │  (Raw)      │───→│ (Cleaned)  │───→│ (Business) │     │
│  │             │    │            │    │            │     │
│  │ 生データ     │    │ クレンジング │    │ ビジネス    │     │
│  │ そのまま保存  │    │ 正規化      │    │ ロジック    │     │
│  │ 追記のみ     │    │ 型変換      │    │ 集計       │     │
│  └────────────┘    └────────────┘    └────────────┘     │
│                                                          │
│  データソース ───→  取り込み ───→  変換 ───→  提供           │
└──────────────────────────────────────────────────────────┘

各層の詳細

目的データの状態スキーマ消費者
Bronze生データの忠実な保存ソースシステムのまま(JSON, CSV等)スキーマオンリードデータエンジニア
Silverクレンジングと正規化型変換済み、重複排除、NULL処理済み明示的スキーマデータアナリスト、データサイエンティスト
Goldビジネスロジックの適用集計済み、KPI計算済み、マート化ビジネス定義に基づくスキーマBIツール、経営層、アプリケーション

Bronze層の設計

設計原則

原則説明理由
忠実な保存ソースデータをそのまま保存後から変換ロジックを変更できる
追記のみ(Append-only)既存データを更新しないデータの監査証跡を保持
メタデータの付与取り込み日時、ソース名、バッチIDを付加トレーサビリティの確保
パーティショニング日付でパーティション分割クエリ効率とコスト最適化
Bronze層のディレクトリ構造:

s3://data-lake/bronze/
├── orders/
│   ├── year=2025/
│   │   ├── month=01/
│   │   │   ├── day=15/
│   │   │   │   ├── orders_20250115_001.parquet
│   │   │   │   └── _metadata.json
│   │   │   └── day=16/
│   │   └── month=02/
│   └── _schema_evolution/
│       └── schema_v1.json
├── customers/
├── products/
└── events/

Bronze層のdbtモデル例

-- models/bronze/brz_orders.sql
-- Bronze: 生データの取り込み(最小限の変換のみ)

{{ config(
    materialized='incremental',
    unique_key='_ingestion_id',
    partition_by={'field': '_ingested_at', 'data_type': 'date'}
) }}

SELECT
    *,
    _FILE_NAME AS _source_file,
    CURRENT_TIMESTAMP() AS _ingested_at,
    {{ generate_surrogate_key(['order_id', '_FILE_NAME']) }} AS _ingestion_id
FROM {{ source('raw', 'orders') }}

{% if is_incremental() %}
WHERE _ingested_at > (SELECT MAX(_ingested_at) FROM {{ this }})
{% endif %}

Silver層の設計

変換ルール

変換カテゴリ具体的な処理
型変換文字列→適切な型”2025-01-15” → DATE型
NULL処理デフォルト値の設定、フラグ付与NULL → “未設定”
重複排除同一レコードの除去order_idで重複排除
正規化値の統一”東京都”/“東京” → “東京都”
バリデーション異常値の検出・除外負の金額を除外
リレーションテーブル間の結合orders + customers
-- models/silver/slv_orders.sql
-- Silver: クレンジングと正規化

{{ config(
    materialized='incremental',
    unique_key='order_id',
    partition_by={'field': 'order_date', 'data_type': 'date'}
) }}

WITH deduplicated AS (
    SELECT
        *,
        ROW_NUMBER() OVER (
            PARTITION BY order_id
            ORDER BY _ingested_at DESC
        ) AS _row_num
    FROM {{ ref('brz_orders') }}
    {% if is_incremental() %}
    WHERE _ingested_at > (SELECT MAX(_updated_at) FROM {{ this }})
    {% endif %}
),

cleaned AS (
    SELECT
        CAST(order_id AS INT64) AS order_id,
        CAST(customer_id AS INT64) AS customer_id,
        CAST(order_date AS DATE) AS order_date,
        CAST(total_amount AS NUMERIC) AS total_amount,
        UPPER(TRIM(status)) AS status,
        COALESCE(currency, 'JPY') AS currency,
        _ingested_at AS _updated_at
    FROM deduplicated
    WHERE _row_num = 1
      AND CAST(total_amount AS NUMERIC) >= 0  -- 負の金額を除外
      AND order_id IS NOT NULL
)

SELECT * FROM cleaned

Gold層の設計

ビジネスロジックの適用

Gold層のパターン説明消費者
ファクトテーブルビジネスイベントの記録(売上、注文等)BIツール
ディメンションテーブルマスターデータ(顧客、商品等)BIツール
メトリクスマートKPI計算済みの集計テーブルダッシュボード
ワイドテーブル分析用にファクトとディメンションを結合データサイエンティスト
-- models/gold/gld_daily_revenue.sql
-- Gold: 日次売上サマリー

{{ config(
    materialized='table',
    partition_by={'field': 'revenue_date', 'data_type': 'date'}
) }}

SELECT
    o.order_date AS revenue_date,
    p.product_category,
    c.customer_segment,
    COUNT(DISTINCT o.order_id) AS order_count,
    COUNT(DISTINCT o.customer_id) AS unique_customers,
    SUM(o.total_amount) AS gross_revenue,
    SUM(o.total_amount) - SUM(COALESCE(r.refund_amount, 0)) AS net_revenue,
    AVG(o.total_amount) AS avg_order_value
FROM {{ ref('slv_orders') }} o
LEFT JOIN {{ ref('slv_customers') }} c ON o.customer_id = c.customer_id
LEFT JOIN {{ ref('slv_products') }} p ON o.product_id = p.product_id
LEFT JOIN {{ ref('slv_refunds') }} r ON o.order_id = r.order_id
WHERE o.status = 'COMPLETED'
GROUP BY 1, 2, 3

層間のデータ品質

各層の品質チェック

チェック内容ツール
Bronze → Silverスキーマ整合性、NULL率、レコード数の整合Great Expectations, dbt tests
Silver → Goldビジネスルール検証、集計値の整合、外部データとの突合dbt tests, custom assertions
# models/silver/slv_orders.yml
# dbt テスト定義

models:
  - name: slv_orders
    description: "クレンジング済み注文データ"
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: total_amount
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: ">= 0"
      - name: status
        tests:
          - accepted_values:
              values: ['PENDING', 'COMPLETED', 'CANCELLED', 'REFUNDED']
      - name: order_date
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: "<= CURRENT_DATE()"

「Medallionアーキテクチャの本質は、データの品質を段階的に向上させることだ。Bronze層で生データを失わず、Silver層で信頼性を確保し、Gold層でビジネス価値を提供する」 — 田中VPoE


まとめ

ポイント内容
MedallionアーキテクチャBronze/Silver/Goldの3層でデータ品質を段階的に向上
Bronze層生データの忠実な保存、追記のみ、メタデータ付与
Silver層クレンジング、正規化、重複排除、バリデーション
Gold層ビジネスロジック適用、KPI計算、マート化

チェックリスト

  • Medallionアーキテクチャの3層の役割を説明できる
  • 各層でのデータ変換内容を理解した
  • dbtを使った各層のモデル設計ができる
  • 層間のデータ品質チェックの実装方法を理解した

次のステップへ

次は「演習:データ戦略ドキュメントを作成しよう」です。Step 1で学んだデータ戦略、アーキテクチャパターン、Data Mesh、Medallionアーキテクチャの知識を統合し、実践的なドキュメントを作成します。


推定読了時間: 30分