こんにちは。テックドクターのエンジニア、伊藤です。
テックドクターでは、心拍や睡眠などの日常の生体データを取得するためにウェアラブルデバイスを活用しています。これらのデバイスで集めたデータを使って、健康管理やフィットネスの向上、さらには医療分野への応用を目指しています。
いま世の中に出回っているウェアラブルデバイスの例としては、 Apple Watch、Garmin、Fitbit などがあります。これらは(理想的には) 24 時間 365 日データを取得し続けることができます。そのデータは同期している iPhone のストレージや各社のサーバーに保存されますが、活用のためにデータを取得したいときはWeb API などを通じて取得することになります。
その際、日付などの条件を指定してデータを取得する以外に、(仕組みが提供されていれば)Webhook などを活用してリアルタイムに近い形でデータを取得することも可能です。
後者のようなリアルタイム/準リアルタイムのデータ取得は、ユーザーの行動変化に即座に対応したい場合や、継続的なモニタリングが必要な場合に特に有用です。ただし、それに対応したデータ収集基盤の構築も必要となってきます。
この記事では、 Fitbit に対してリアルタイムでデータ収集をする際の基盤設計案を紹介したいと思います。なお、今回は Fitbit を例としますが、似たような構成の API や Webhook 機能を提供しているサービスであれば、同様のアーキテクチャを適用可能だと思います。
Fitbitデータ収集における課題
まずは、今回のデータ取得の課題について整理しておきましょう。ウェアラブルデバイスという特性上発生する課題と、 Fitbit API による課題があります。
課題
- データ更新の頻度: ユーザーの日次データ(歩数、睡眠時間等)は一日中継続的に更新される
 - 遅延データの到着: デバイス同期のタイミングにより、過去日時のデータが後から送信される場合がある
 - レート制限: Fitbit API には厳格な制限があり、効率的なデータ収集戦略が必要
 - データ一貫性: リアルタイム性とデータ整合性のバランス
 - スケーラビリティ: ユーザー数の増加に対応可能な設計
 - コスト最適化: 不要な API 呼び出しの削減
 
ここで注目してほしいのが最初の2つ「データ更新の頻度」「遅延データの到着」です。実はこれらの特性にぴったりの強みを持ったプラットフォームがあります。それが Apache Hudi です。
Apache Hudi とは?
Apache Hudi が何であるか、まずは公式ドキュメントから引用します。
What is Apache Hudi
Apache Hudi (pronounced "hoodie") pioneered the concept of "transactional data lakes", which is more popularly known today as the data lakehouse architecture. Today, Hudi has grown into an open data lakehouse platform, with a open table format purpose-built for high performance writes on incremental data pipelines and fast query performance due to comprehensive table optimizations.
全訳すると長くなるのでかいつまんで説明すると、Hudi(フーディ)はデータレイクハウスアーキテクチャを実現するためのオープンソースプロジェクトです。インクリメンタルなデータパイプラインに適していると説明されています。
また、AWSのドキュメントでは、センサーやIoTデバイスからのストリーミングデータに対して、特定のデータ挿入と更新イベントが必要な場合にHudiが適していると述べられています。
Working with streaming data from sensors and other Internet of Things (IoT) devices that require specific data insertion and update events.
引用元:AWS EMR Hudi
Hudiのこれらの強みは、まさに今回の用途、ウェアラブルデータを「準リアルタイム」に取得し「同一レコードの頻繁な更新」をする際にとても有効です。
Hudiは他にもタイムトラベル機能やスキーマ進化のサポートなど、データレイクハウスの運用に必要な機能もいろいろと備えています。
というわけで、今回はマイクロサービスアーキテクチャと Apache Hudi を組み合わせ、リアルタイム通知をマイクロバッチ処理へ変換する仕組みを考えてみることにしました。
※ただし、本記事は実装前の設計案であり、実際の運用環境での検証は今後の課題です。
Fitbit Subscription APIについて
Subscription APIとは
アーキテクチャ全体の説明に入る前に、Fitbit 側のAPIについても説明しておきます。
Fitbit Subscription APIは、ユーザがFitbitアプリを通じてFitbitサーバーにデータを同期したときに、事前に登録したサーバーにWebhookを通じて通知を送信するAPIです。これを利用することで、データの変更を即座に検知し、必要なデータ取得をトリガーできます。
通知データの構造
Subscription APIからの通知は以下のJSONフォーマットで受け取ります。
[ { "collectionType": "foods", "date": "2010-03-01", "ownerId": "USER_1", "ownerType": "user", "subscriptionId": "1234" } ]
主要フィールドの説明
| フィールド | 説明 | 例 | 
|---|---|---|
| collectionType | データの種類 | `foods`, `activities`, `sleep`, `heartrate` | 
| date | データの日付 | `2010-03-01` | 
| ownerId | ユーザー識別子 | `USER_1` | 
| ownerType | オーナータイプ | `user` (固定値) | 
| subscriptionId | サブスクリプション識別子 | `1234` | 
注目して欲しい点は、通知自体には詳細なデータは含まれず、あくまで「どのユーザーのどのデータタイプが更新されたか」の情報のみであることです。実際のデータ取得にはユーザー・collectionType・日付に基づいて、別途 API 呼び出しが必要です。
Subscription API利用時の制約事項
前述したように、API 呼び出しには制限があります。
レート制限:
150 requests/hour/user
レスポンスタイムアウト:
- Fitbitは通知送信時に5秒のタイムアウトを設定
 - エンドポイントは迅速な応答(204 No Content)が必要
 - これらが守られない場合、最終的にサブスクリプションを無効化する可能性がある
 
以上が Fitbit Subscription API の概要です。
アーキテクチャの全体像
全体のシステム構成図
いよいよシステムの全体像です。
大きく以下の2つの部分に分けられます。
①Fitbit Subscriptionからの通知を受け取り永続化する
②ミニバッチでAPI呼び出しを行い、データをApache Hudi形式で永続化する
クラウド基盤としては Google Cloud を使用します。

各コンポーネントについてまとめます。
①通知処理部分
| インフラ | (コンポーネント名) | 役割 | 主な責務 | 
|---|---|---|---|
| Cloud Run | Gateway | Notification 受信専用 | 高速応答、Pub/Sub 転送 | 
| Cloud Pub/Sub | メッセージング基盤 | 非同期処理 | |
| Cloud Run | Ingestor | 永続化サービス | NotificationのGCS への書き込み、メタデータ付与 | 
| Cloud Storage (GCS) | Notification 永続化 | Notification の蓄積 | 
②ミニバッチ部分
| インフラ | (コンポーネント名) | 役割 | 主な責務 | 
|---|---|---|---|
| Cloud Scheduler | 定期実行 | バッチ処理トリガー | |
| Cloud Run | Batch | バッチ処理サービス | GCS からのデータ取得、API 呼び出し | 
| Dataproc + Apache Hudi | データレイクハウス | 変換、保存 | |
| Cloud Storage | ウェアラブルデータ永続化 | ウェアラブルデータの蓄積 | 
このような構成です。
次の項でそれぞれもう少し詳しくご説明していきます。
アーキテクチャ設計の詳細
Notification 受信層(Cloud Run + Pub/Sub)

Fitbit Subscription API の制約として、5 秒以内に 204 No Content を返すサービスを実装する必要があります。
そのためここでは通知の受信と Pub/Sub への Publish のみに特化した軽量サービスを Cloud Run で構築します。
処理の流れは以下の通りです。
- Fitbit からの Webhook 通知を HTTP POST で受信
 - 受信した JSON データを最小限の検証のみ実施
 - タイムスタンプを付与して Pub/Sub メッセージとして即座に Publish
 - Fitbit に対して 204 No Content レスポンスを返却(5 秒以内)
 
Notificationデータ 永続化層(Cloud Run + GCS)

この層では Pub/Sub から受信したメッセージを構造化して GCS に保存します。バッチ処理に適した形でデータを整理し、後続のバッチジョブが効率的に処理できるようにします。
処理の流れは以下の通りです。
- Pub/Sub からメッセージを受信
 - メッセージのパースと検証
 - バッチ ID と受信タイムスタンプを付与
 - GCS に日付・時間ベースでパーティショニングして保存
 
GCS 保存例:
/notifications/ ├── year=2025/ │ ├── month=08/ │ │ ├── day=01/ │ │ │ ├── hour=00/ │ │ │ │ └── 01K1GFYJHR7EKM5HN9PPD2AG9V.json │ │ │ │ ├── 01K1GFYKHKJ3KHKVY0RDGNE2GZ.json │ │ │ └── hour=01/ │ │ └── day=02/ │ └── month=09/
保存データ形式:
{ "original_notifications": [...], "metadata": { "received_at": "2025-08-01T14:00:00Z", "batch_id": "01K1GFYJHR7EKM5HN9PPD2AG9V" } }
バッチ処理層(Cloud Run + Dataproc + Apache Hudi)

この層では蓄積された通知情報をもとに、ミニバッチで API 呼び出しを行います。その結果を Apache Hudi 形式でデータレイクハウスとして保存します。
処理の流れは以下の通りです。
- 未処理通知の発見: GCS から新しい通知ファイルを検出
 - データのグルーピング: ユーザー・日付・データタイプで最適化されたバッチを作成
 - Fitbit API の呼び出し: レート制限を考慮した効率的なリクエスト実行
 - Apache Hudi での書き込み: Upsert 操作によるデータレイクハウスへの永続化
 
データ管理のポイントとしては簡単に以下のようなものがあります。
- パーティショニング戦略: 日付ベースでのデータ配置最適化
 - 主キー制約: ユーザーID, データタイプ, 日付の組み合わせによる一意性保証
 - 更新時刻管理: 最新データを保持
 
アーキテクチャ全体の説明は以上です。
考慮事項と今後の課題
ここまでで全体的な設計案を示しましたが、実際の運用に向けてはより詳細な検討が必要になる点もあります。
例えばバッチ処理層では、単なるAPI呼び出しを行うような書き方をしましたが、実際には蓄積された通知をグルーピングして API 呼び出しを行う際にレート制限を超えないようにするための工夫が必要です。
また、繰り返しになりますがFitbit は「データが更新された」という通知のみを送信し、実際のデータは別途 API で取得する仕組みです。しかし、他のウェアラブルデバイスでは、通知と同時に実際の測定データ(心拍数、歩数など)をストリームとして直接送信する場合があります。その場合、このアーキテクチャに沿って単にそのデータを保存するだけだと、障害などでデータを取り逃がした際にデータ損失を起こす可能性があります。別途API での再取得機能を実装したり、データの完全性を担保する仕組みが必要になるでしょう。
以上、参考になれば幸いです。
