ファインディのKPIダッシュボードを支えるLookerと段階的データモデリング戦略

こんにちは。ファインディでデータエンジニアをやっている開(hiracky16)です。

ファインディでは事業の成長に伴い、スプレッドシートで管理していたKPIダッシュボードの複雑さが限界を迎えつつありました。この記事ではLookerを導入し、derived_table→mart→dim/factと段階的にデータモデリングを進化させてきた過程を紹介します。ファインディが大切にしているバリューの一つであるスピードを損なわずにガバナンスを整えていくノウハウとして、参考になれば幸いです。

ファインディにおけるKPIダッシュボードの重要性と複雑性

ファインディは主に4つの事業と8つのサービスを展開しています。事業ごとに追うべきKPIが異なるため、事業部内のメンバーはもちろん、複数チームや事業を横断して見る部長や役員にとっても、各事業の健康状態を把握できるダッシュボードの必要性が以前と比べて増しています。事業によって売上が発生するまでの経路やリードタイムが異なり、ユーザーや企業を独自のセグメントで切り分けて分析する必要もあります。こうした複雑さは事業が成長するごとに増していきます。

たとえばファインディの祖業である転職事業では、職種・経験年数・アクセス頻度などといった属性値をもとに多軸で分析を行っています。さらに、転職に至るまでにはスカウト・応募・面談といった複数のファネルが並行して進むため、単純な線形のファネル分析では実態を捉えきれない難しさがあります。

Looker導入の背景

こうした重要性や複雑性を踏まえつつ、既存のスプレッドシートとBigQuery構成でKPIを管理する方法にはいくつかの課題が生じていました。

まず、事業やチームごとにスプレッドシートが増え続け、それぞれが独自フォーマットで作られているため、チームを横断した利用ができない状態でした。期が変わるたびに指標が追加されていくと、今期追うべき指標の整理も難しくなります。さらに、スプレッドシートごとにセグメントの切り方や集計ロジックが異なり、同じ指標のはずなのに数値が一致しないといった問題も起きていました。加えて、データ量の増加に伴いスプレッドシートの表示速度が低下し、売上管理の集計結果表示で障害が発生するなど、安定性の面でも限界が見え始めていました。

こうした課題を解決するために、LookMLで指標定義をコード管理しGitHubでレビューできるLookerを導入しました。BigQueryとネイティブに連携できるためデータ量が増えても表示速度が安定すること、チーム内にLookerの経験者がいて立ち上げコストを抑えられること、そしてGoogleの担当の方による丁寧なPoCで運用イメージが具体化できたことが導入の決め手になりました。

段階的なデータモデリングの最適化

導入後、まずは主要なKPIがLookerに集まっている状態を目指しました。ただし、利用者がBIツールに求めているのはいち早く事業数値を確認することであり、指標の定義を統一したいというデータチーム側の課題意識とは温度差があります。スプレッドシートからの移行でスピード感を落とさないことを重視しつつ、段階的にデータモデリングを進化させていきました。

全体の流れを図にすると次のようになります。

段階的なデータモデリングの全体像

derived_tableからの出発

最初のステップとして採用したのが、LookMLのderived_tableです。derived_tableはSQLベースでビューを定義できるため、BigQueryのSQLに慣れ親しんだメンバーにとってはダッシュボードやLook構築までのリードタイムが最も短い方法でした。ファインディではデータ変換にDataform(プロジェクトによってはdbt)を使っていますが、LookMLとは別リポジトリで管理する必要があり、両方を行き来すると開発速度が落ちてしまいます。その点derived_tableであればLookMLリポジトリだけで完結するため、スプレッドシートからの移行初期にはこの手軽さが大きな武器になりました。

一方で、derived_tableはExploreが実行されるたびにBigQueryへクエリが走るため、データ量が増えるにつれて表示速度が遅くなり、クエリコストもかさむという課題が見えてきました。

たとえばDAU(Daily Active User)を集計するビューは次のようなイメージです。

view: daily_active_user {
  derived_table: {
    sql:
        WITH calendar AS ( ... ),
        page_view AS ( ... ),
        users AS ( ... )
        SELECT calendar.report_date,
            users.user_job_segment,
            users.user_id
        FROM calendar
        LEFT OUTER JOIN page_view ON ...
        LEFT OUTER JOIN users ON ...
    ;;}

  dimension_group: report { ... }
  dimension: user_job_segment { ... }

  measure: dau { ... }
}

explore: daily_active_user {}

martレイヤーの導入

derived_tableの課題を解消するため、SQLのロジックをDataformへ移行し、事前に計算済みのmartテーブルとしてBigQuery上に実体化しました。LookML側はderived_tableのSQL定義を削除し、sql_table_nameでmartテーブルを参照する形に書き換えます。Exploreからは計算済みのテーブルを読むだけになるため、表示速度とクエリコストの両方が改善されます。

先ほどのDAUの例をmartに移行すると、LookML側は次のようにシンプルになります。

view: daily_active_user {
  sql_table_name: `project.mart.daily_active_user` ;;

  dimension_group: report { ... }
  dimension: user_job_segment { ... }

  measure: dau { ... }
}

explore: daily_active_user {}

derived_tableのSQL定義がなくなり、sql_table_nameでmartテーブルを参照するだけになっています。ただし、martテーブルはスケジュール実行で更新するため、データの鮮度には注意が必要です。先ほどのDAUの例であれば日次の集計で十分なので、Dataformのスケジュール実行で一日一回テーブルを更新しています。

dim/factモデルへの移行

martテーブルが複数できあがってきたタイミングで、ディメンショナルモデリングへ進みます。ここで重要なのは、すでに動いているレポートが複数ある状態でモデリングを行うことです。実際のレポートから共通する軸を抽出できるため、机上の設計よりも実態に即したモデルを組み立てられます。

たとえば、先ほどのDAUレポートに加えて、コンバージョンを日次で追うレポートも職種セグメント別に組み立てられているとします。この場合、user_job_segmentは複数のレポートで共通して使われる適合ディメンション(Conformed Dimension)になります。日付軸も同様です。

こうした共通軸を整理していくと、ユーザーディメンション・カレンダーディメンション・ページビューファクトという形に分解できます。ディメンションとファクトに分けたことで、新しいレポートを追加する際も既存のディメンションを再利用でき、定義のばらつきを防げるようになりました。

分解後のLookMLは次のようになります。

# カレンダーディメンション
view: dim_calendar {
  sql_table_name: `project.dim.calendar` ;;

  dimension_group: report { ... }
}

# ユーザーディメンション
view: dim_user {
  sql_table_name: `project.dim.users` ;;

  # サロゲートキーを作成
  dimension: user_key { ... }
  dimension: user_job_segment { ... }
}

# ページビューファクト
view: fct_page_view {
  sql_table_name: `project.fct.fct_page_views` ;;

  dimension: user_key { ... }
  dimension: user_id { ... }
  dimension: access_date { ... }
  measure: dau { ... }
}

# コンバージョンファクト
view: fct_conversion {
  sql_table_name: `project.fct.fct_conversions` ;;

  dimension: user_key { ... }
  dimension: conversion_date { ... }
  measure: conversions { ... }
}

# Explore でディメンションとファクトを結合
explore: fct_page_view {
  label: "ページビュー分析"
  join: dim_calendar { ... }
  join: dim_user { ... }
}

explore: fct_conversion {
  label: "コンバージョン分析"
  join: dim_calendar { ... }
  join: dim_user { ... }
}

fct_conversionのExploreでもdim_calendardim_userをそのまま再利用しています。ファクトテーブルが増えてもディメンションを使い回せるため、定義の重複が起きません。

移行時のガードレール整備

dim/factモデルへの移行ではBigQueryのテーブル構造とLookMLの両方に修正が入るため、意図せずダッシュボードが壊れるリスクがあります。ファインディではDataform上でのmartテーブルとdim/factテーブルの整合性テスト、LookerのCIによるLookMLバリデーション、そしてLooker APIを使ったExplore実行テストの3つをガードレールとして整備しました。

zenn.dev

特にDataformやLookMLはClaude Codeなどのコーディングエージェントを活用して開発する場面が増えており、こうしたガードレールがあることで開発速度を維持しながら安全に移行を進められています。

データアナリストの自律化と利用状況の変化

導入から1年弱が経った現在、全体のダッシュボードのうち68%がデータエンジニア以外のメンバーによって作成されたものになりました。データアナリストや事業部のメンバーがExploreを使って自らダッシュボードを組み立てられるようになり、「データを見たいときにデータチームへ依頼する」というボトルネックが解消されつつあります。

利用率も好調で、登録ユーザーの約7割が毎週ログインしてダッシュボードを確認しています。ただしLookerはライセンス費用がかかるため、現時点ではアカウントを発行できているのは一部のメンバーに限られています。今後はアカウント数を増やしつつ、SQLを触れないメンバーにもExploreを使って独自のコンテンツを作ってもらえるよう支援を広げていきたいと考えています。

こうした利用状況の把握にはLookerのSystem Activityを活用しています。System ActivityはLooker自体の利用データをそのままExploreとして扱えるため、誰がどのダッシュボードをどれくらい使っているかを日々モニタリングできます。Lookerは高機能な分コストもかかるからこそ、利用状況を定量的に可視化して成果をアピールし続けることが重要です。

まとめ

derived_table→mart→dim/factと段階を踏むことで、利用者のスピード感を損なわずにガバナンスを整えられました。ダッシュボードの68%がデータエンジニア以外のメンバーによる作成となり、データチームへの依頼ボトルネックの解消につながっています。

また整えた指標をチームや事業を越えて利用するシーンも増えてきた反面、モデリングの変更が広範囲に影響するようになりました。ガードレールの重要性は今後さらに増していくと感じています。LookMLで指標定義を整えてきた土台は、Lookerの会話分析のような生成AI機能の精度にも直結するため、モデリングの品質を高め続けることがデータ活用全体の鍵になると考えています。

ファインディでは絶賛データエンジニアを募集中です。 LookMLを使ったガバナンス強化やファインディの展開している事業領域におけるデータモデリングに興味を持っていただいた方はこちらのページからご応募お願いします。

herp.careers