[ Data Infrastructure ]

データ収集と利用を分離する — Yakumo の金融データ基盤と動画生成エンジンの連携設計

収集ロジックと利用ロジックを分離することは、最初から設計に組み込むべき原則であり、スプレッドシートという単純なインターフェースを介した疎結合設計で実現できる。

Author: 森本拓見 Updated: May 11, 2026
#claude-code #ai-driven-dev #data-engineering #architecture

複数の独立したプロダクトやサービスを組み合わせるとき、避けられない判断がある。データを集めるシステムと、それを消費するシステムをどこで分けるかだ。

最初は「同じモジュールに書いたほうが効率的では」という誘惑に駆られる。しかし、その判断がもたらす後悔は大きい。収集ロジックが変わるたびに利用側が壊れ、利用側の要件が変わるたびに収集側に手を入れる。変更コストが落ちない設計は、組織の負債となる。

この記事では、収集と利用を徹底的に分離する設計パターンを、その実装アプローチと運用を通じて整理する。


1. 課題:責務混在による変更の破壊性

スクレイピングやデータベース連携を扱うシステムでよく起きるのは、次のような光景だ。

「データ収集パイプラインが、集めたデータをそのまま利用層向けに整形する」

短期的には効率的に見える。ファイルを跨がず、一連のプロセスで完結する。しかし、数ヶ月後に収集元の仕様が変わったとき、ツケが来る。

具体例:金融データを複数のソース(証券取引所、企業の決算説明資料、株価データベース)から集めるシステムがあるとしよう。最初は、収集スクリプトが取得したデータをそのまま整形し、マスターテーブルに格納していた。ところがある日、取得元の URL 構造が変わり、パーサーを全面改修する必要が生じた。

その過程で「ちょっと待てよ、このカラムの順番、利用側が期待している順序と違わないか」という問題が浮上する。収集側を修正しながら、同時に利用側の参照部を追い直さなければならない。

逆方向でも同じ。利用側(例えば、可視化やレポート生成のシステム)が「このデータもあったらいいな」と新しいカラムを要求すると、収集側のスクリプト全体に影響が波及する。

これは設計の根本的な問題だ。責務の境界が曖昧だと、どちらか一方の変更がもう一方に自動的に破壊をもたらす。


2. パターン定義:責務の完全分離

解決策はシンプルにして明確だ。

収集層の責務:データソースから情報を取得し、一貫した構造で蓄積する。品質と完全性にのみ責任を持つ。

利用層の責務:蓄積されたデータを読み取り専用で参照し、自層の目的に応じて消費する。データ構造の上流変更に依存しない。

この分離の鍵は、層間のインターフェースを明確に定義することにある。

データベーステーブル、API スキーマ、スプレッドシートのカラム構造——形は何でもいい。重要なのは、インターフェースが明示的で、変更を追跡できることだ。


3. 設計の核:スプレッドシートを選んだ理由とトレードオフ

八雲では、データ収集基盤(medallion と呼ぶ)と動画生成エンジン(montage)を独立したプロジェクトとして運用している。両者の間を繋ぐインターフェースは、共有 Google スプレッドシートだ。

なぜスプレッドシートか。一見、原始的に思えるこの選択には、実装上の強い利点がある。

メリット:

  1. スキーマを明文化しやすい:カラム名、型、必須フィールドをスプレッドシート上で一目で確認できる。「新しいカラムを追加するときは必ず仕様書を更新する」というルールを自然と適用できる。

  2. インターフェース変更が検視可能:REST API のエンドポイント設計や DB スキーマの ALTER TABLE と異なり、スプレッドシートの変更は人が読めて、差分が明確だ。「このバージョンでは何が変わったか」を履歴で追える。

  3. 将来の API 移行パスが開かれている:後述するが、スプレッドシートを介した責務分離を確立すれば、REST API や MCP サーバへの拡張は「インターフェース層を追加するだけ」で済む。コアロジックを書き直す必要がない。

デメリット・トレードオフ:

  1. スケールの限界:HUMAN_INPUT — スプレッドシートの行数制限やセル数上限は、大規模運用では制約になる可能性がある(具体的な限界値や現在の運用データ規模の詳細が必要)。

  2. 同時実行性の欠如:複数のプロセスが同時にスプレッドシートに書き込むとき、排他制御がない。HUMAN_INPUT — 実際の同時実行数や応答時間の影響測定があれば、トレードオフを定量化できる。

  3. リアルタイム性:API なら HTTP で即座にアクセスできるが、スプレッドシートは Google Workspace の同期遅延を被る。HUMAN_INPUT — 実運用での遅延時間データがあれば根拠が強まる。

それでも八雲がこれを選んだのは、「シンプルさ」がもたらす設計の堅牢性が、スケールのハンディキャップを上回ると判断したからだ。スキーマを極限まで簡潔に保つことで、責務分離の原則が破られにくくなる。


4. Yakumo での適用:medallion と montage の実装パターン

収集層(medallion):証券取引所の決算短信、企業の決算説明資料、各種財務データベースから構造化情報を抽出。銘柄属性マスターと、年度別タブ(決算期ごとに損益計算書、貸借対照表、キャッシュフロー計算書を蓄積)で構成されるスプレッドシートに書き込む。約 3,800 社、過去 5 年分の対応を見込む。

利用層(montage):スプレッドシートを読み取り専用で参照し、動画コンテンツを生成。特定銘柄の特定年度のデータを取得する薄いクライアント(sheets_io.py)を経由して、型付きの構造体として利用層に返す。

このクライアントが「薄い」ことが決定的に重要だ。データ取得と返却だけを担い、変換・整形ロジックを一切含まない。ロジックをクライアントに書き始めた瞬間、責務の境界が崩れる。

他のツール(GitHub 上の hub リポジトリ、xbrl-parser)の成果も、このクライアント経由で統一的にアクセスできる。タブ構造が増えても(管理ツール sheets-yearly-tabs で自動化)、利用側のコードは変わらない。


5. 運用が明かした落とし穴と解決策

落とし穴 1:クライアント層への責務汚染

「ちょっと整形するだけ」という小さな判断から始まる汚染。クライアント側に「スプレッドシートのカラム A と B から計算値 C を導出する」というロジックが混入すると、その瞬間から収集側の変更が利用側に波及し始める。

解決策:「取得と返却のみ」というルールを明文化し、コードレビューでチェックリストに加えた。

落とし穴 2:スプレッドシートが暗黙のスキーマ化

カラム名、並び順、タブ名が「事実上のインターフェース契約」になるのは避けられない。しかし、これを明示的に文書化しないと、「なぜこの順序なのか」「このカラムは何を表すのか」という疑問が後発のメンバーに生じ、スキーマ変更が怖くなる。

解決策:specs/system/earnings-schema.md にスキーマを明文化。新しいカラムを追加するときは、必ず先に仕様書を更新してから、スプレッドシートに反映するというプロセスに統一した。

落とし穴 3:冪等性の欠如による重複書き込み

launchd による自動実行と手動実行が並走するとき、同じデータが二重に格納される問題が起きた。銘柄×年度×四半期をキーにした冪等写入に統一することで解決した。

HUMAN_INPUT — 実際の重複発生頻度や、冪等性の実装にかかったコスト(工数・複雑性の増加)があれば、より説得力がある。


6. 将来への道:REST API と MCP サーバへの拡張余地

収集と利用を分離した設計は、将来の拡張に強い。

現在は gws CLI 経由のシェルベースだが、medallion に REST API 層を被せることを検討している。GET /earnings/{ticker}/{year}/ のようなエンドポイントを公開すれば、ブラウザや外部ツールからも直接アクセスできる。

さらに先の未来では、MCP(Model Context Protocol)サーバとして公開することも視野に入れている。Claude Code から直接金融データを参照しながらコンテンツを生成する——そうした流れがスムーズに繋がる。

重要なのは、どの拡張を選んでも、medallion のコアロジック(収集・パース・蓄積)を書き直す必要がないということだ。インターフェース層の追加だけで済む。

責務分離が成功している設計とは、「変更に強い」のではなく「層を追加しやすい」状態だ。


まとめ

収集と利用の分離は「いずれやるべきリファクタリング」ではなく、最初から設計に組み込むべき原則だ。

独立したプロジェクトとして物理的な境界を引き、その間を明確で変更追跡可能なインターフェースで繋ぐ。スプレッドシートのようにシンプルなメディアを選ぶことで、スキーマの明文化が強制される。複雑なインターフェース設計は不要だ。

八雲の事例では、このアプローチによって収集パイプラインのリファクタリングが怖くなくなり、利用側は「データ構造が安定している」という前提のもとで機能追加に集中できるようになった。

何より、責務を分離しておいたからこそ、将来の拡張パス(REST API、MCP サーバ、さらには外部への提供)が塞がれていない。シンプルな設計は、スケールの余地も残す。

ShareShare on X