モバイルアプリやWebサービスのバックエンドとして、Firebaseが提供するデータベースサービスを検討する際、必ず直面するのが「FirestoreとRealtime Database、どちらを選ぶべきか」という問題です。
両者ともにサーバーレスでリアルタイム同期を謳いながらも、そのアーキテクチャと特性は根本的に異なります。
この選択を誤ると、スケーラビリティの壁にぶつかったり、想定以上の運用コストが発生したりすることも珍しくありません。
結論から言えば、新規プロジェクトの大部分においてFirestoreが推奨されるデファクトスタンダードです。
その最大の利点は、柔軟なデータモデリングと強力なクエリ機能、そして自動スケーリングの3つに集約されます。
Realtime DatabaseがJSONツリーという単一構造に依存しているのに対し、Firestoreはコレクションとドキュメントによる階層構造を持ち、かつインデックスを自在に組み合わせた複合クエリが可能です。
- データモデリングの柔軟性:サブコレクションを使ったネスト構造や、ルートレベルの複数コレクション間でのJOINに近い参照設計が容易
- クエリの表現力:where句による複数条件フィルタリング、orderByとの併用、範囲指定(<, <=, >, >=)を同一クエリで実行可能
- スケーラビリティ:書き込みスループットが自動で拡張され、ピーク時でもシャーディング設計が不要
とはいえ、Realtime Databaseにも強みはあります。
レイテンシが極めて低く(多くの場合でFirestoreより数十ミリ秒高速)、オフライン時の書き込み競合を解決するトランザクション処理がより直感的です。
また、データ構造が単純なため、小規模なチャットやゲームのセッション管理などでは、むしろ導入コストが低くなります。
では、具体的にどのようなユースケースで使い分けるべきでしょうか。
以下の表は、評価軸ごとに両者を比較したものです。
| 評価軸 | Firestore | Realtime Database |
|---|---|---|
| データモデル | コレクション/ドキュメント(階層的) | JSONツリー(単一ノード) |
| 複合クエリ | サポート(複数条件+ソート) | 非サポート(単純な絞り込みのみ) |
| スケーリング上限 | 自動(実質無制限) | 上限10万同時接続(クラスタリング要) |
| 読み取りコスト | ドキュメント単位(柔軟) | データサイズ単位(全取得リスク) |
| オフライン永続化 | 標準対応(自動キャッシュ) | 標準対応(手動制御が可能) |
この比較から見える最適なユースケースの境界線は明確です。
Firestoreはソーシャルフィード、Eコマースの商品カタログ、ダッシュボード集計、ユーザープロフィール管理など、多様なアクセスパターンと動的なフィルタリングが求められるシステムに適合します。
一方、Realtime Databaseはオンラインゲームのスコアボード、デバイス間のステータス同期、VoIPシグナリングなど、超低遅延かつデータ構造が固定されたシンプルなユースケースが適地です。
例えば、Firestoreで「過去7日間に投稿された、カテゴリが’tech’で、いいね数が100以上の記事を新しい順に10件」というクエリは、インデックスを1つ定義するだけで実現できます。
Realtime Databaseで同様の処理を書こうとすれば、アプリケーション側で全件フィルタリングとソートを実装せざるを得ず、データ量が増えるとパフォーマンスが著しく劣化します。
つまり、「将来的なデータ拡張やクエリ要件の変化を見越すなら、迷わずFirestoreを選ぶ」 のが、私の実践的なアドバイスです。
ただし、プロトタイプ段階で接続数が極めて少なく、かつオブジェクト構造が完全に静的であると確信できる場合に限り、Realtime Databaseのシンプルさは開発速度の面で依然として有効です。
それでも、運用開始後の仕様変更を考慮すれば、ほとんどのケースでFirestoreのほうがトータルコストで勝ると言えるでしょう。
最後に、価格モデルにも留意してください。
Firestoreは読み取り・書き込み・削除の各操作に対してドキュメント単位で課金されるため、不要なフィールドを取得しない設計が必須です。
Realtime Databaseは転送データ量と保存容量で課金されるため、頻繁に全体を購読するような実装では予想外の出費を招きます。
この違いを理解した上で、自分のシステムが「複雑な検索」と「単純なリアルタイム通知」のどちらに重きを置くのかを冷静に判断することが、成功への第一歩です。
- FirestoreとRealtime Database、そもそも何が違うのか?-アーキテクチャの根本比較
- FirestoreがRealtime Databaseに圧勝する3つのポイント
- 逆転のポイント?Realtime Databaseが未だに選ばれる3つのシナリオ
- オフライン競合解決の直感性-トランザクション処理の差異を検証する
- 実プロジェクトで見る料金モデルの落とし穴-ドキュメント課金 vs 転送量課金
- Firestore移行時に注意すべき設計パターン-正規化と非正規化のバランス
- ユースケース別最適選択マトリクス-これで迷わない実践的な判断基準
- まとめ:将来を見据えた選択とは?Firestoreが持つエコシステム的優位性
FirestoreとRealtime Database、そもそも何が違うのか?-アーキテクチャの根本比較

Firebaseが提供する2つのデータベースサービスは、どちらも「リアルタイム同期」というキャッチコピーを掲げながら、その内部構造は想像以上に異なります。
この違いを理解せずに使い始めると、開発中盤で「思ったようにクエリが書けない」「データが思い通りに正規化できない」という壁にぶつかることになります。
まずは、両者のアーキテクチャをデータモデルとインデックス戦略の観点から解剖してみましょう。
データモデルの違い:ツリー構造 vs コレクション/ドキュメントモデル
Realtime Databaseの核は、単一の巨大なJSONツリーです。
すべてのデータがルートノード以下にネストされ、パス(例:/users/uid/profile/name)でアクセスします。
この構造は直感的で学習コストが低い反面、深いネストはデータ取得時の不要な転送を招き、また、あるノード配下の全データが常に一括で読み取られるという特性があります。
例えば、/users/uidを参照すれば、そのユーザーのプロフィール、設定、投稿履歴まですべてが一度にダウンロードされる可能性があるのです。
一方、Firestoreはコレクション(集合)とドキュメント(文書) という階層を持ちます。
コレクションはドキュメントの入れ物であり、各ドキュメントはフィールドと値のペアを持ちます。
さらに、ドキュメント配下にサブコレクションを配置できるため、階層自体は持てるものの、読み取りはドキュメント単位で行われ、親子関係があっても自動的に子まで取得されることはありません。
この設計により、必要なデータだけをピンポイントで取得できるのが大きな特徴です。
- Realtime Database:データ全体が1つのツリー。パス指定でノード単位取得。子孫データも含めて全取得される
- Firestore:コレクション→ドキュメント→サブコレクション。読み取りはドキュメント単位。明示的にサブコレクションを参照しない限り、子データは取得されない
この違いは、データの粒度に対する制御性に直結します。
Firestoreのほうが「何を読み取るか」を細かく設計できるため、転送量とコストの両面で有利に働くケースがほとんどです。
インデックスとクエリエンジンの設計思想
次に決定的に異なるのが、クエリの表現力です。
Realtime Databaseは、特定のパスに対する単純なフィルタリング(orderByChildとlimitToFirst/Last) しかサポートしません。
複数条件での絞り込み(例:年齢が20以上かつ出身地が東京)を実現するには、クライアント側で全データを取得してからメモリ上でフィルタリングするしかなく、データ量が増えると著しいパフォーマンス劣化を招きます。
Firestoreは、複合インデックスを事前に定義することで、where句による複数条件フィルタ、orderByとの併用、範囲比較(<、<=、>、>=)を同一クエリで実行できます。
内部的には、インデックスエンジンがB-Tree構造を持ち、スキャン範囲を劇的に絞り込むことが可能です。
例えば、以下のようなクエリはRealtime Databaseでは事実上実装不可能ですが、Firestoreではインデックスを1つ追加するだけで動作します。
// Firestoreの複合クエリ例
const snapshot = await db.collection('articles')
.where('category', '==', 'tech')
.where('likes', '>=', 100)
.orderBy('publishedAt', 'desc')
.limit(10)
.get();
このクエリは、「カテゴリが’tech’でいいね数が100以上の記事を、公開日の新しい順に10件」という条件を、サーバー側でインデックスだけを走査して返します。
Realtime Databaseで同様の処理を書くなら、全記事をダウンロードした後、アプリケーション層で3段階のフィルタとソートを実装する必要があり、データが1万件を超えた時点で現実的ではなくなります。
スケーリングアーキテクチャの根本的差異
もう一つ見逃せないのが、スケーリングの振る舞いです。
Realtime Databaseは単一ノード上で動作し、書き込みスループットは1ノードあたり約1,000件/秒が上限とされています。
これを超える場合は、アプリケーション側でシャーディング(データを複数のDBインスタンスに分散)を自前実装する必要があり、運用負荷が跳ね上がります。
Firestoreは分散型のマルチリージョンアーキテクチャを採用し、データが自動的にパーティショニングされます。
書き込みスループットは理論上無制限に拡張可能で、特定のホットパーティションが発生した場合も、内部で自動的に分割が行われます。
開発者がシャーディングキーを意識する必要は一切ありません。
この差は、サービスが成長した後の運用コストと信頼性に直結するため、中長期的な視点では極めて重要な判断基準となります。
以上のアーキテクチャ比較から、Firestoreは「複雑なデータアクセスパターン」と「将来の拡張性」を重視するモダンなアプリケーションに適合し、Realtime Databaseは「超シンプルな構造」と「超低遅延」が最優先される特定領域に適していると言えます。
次のセクションでは、このアーキテクチャ差が実際の開発体験にどう影響するかを、より具体的なユースケースとともに掘り下げていきます。
FirestoreがRealtime Databaseに圧勝する3つのポイント

アーキテクチャの違いを理解したところで、実践的な開発現場においてFirestoreがRealtime Databaseを凌駕する具体的な3つのポイントを提示します。
これらは単なる機能比較ではなく、開発生産性・運用コスト・将来の拡張性という現実的な指標で評価したものです。
それぞれのポイントを順に掘り下げていきましょう。
クエリの表現力で勝負する-Firestoreの強力なフィルタリングとインデックス戦略
先述の複合クエリに加え、Firestoreが真価を発揮するのは複数フィールドに対する不等号条件とソートの同時実行です。
Realtime DatabaseではorderByChildで指定できるキーは1つだけで、その後にlimitを組み合わせるのが限界でした。
Firestoreでは、最大で10個のフィルタ条件を組み合わせられ、かつ各フィールドに対して不等号(<, <=, >, >=)と等価(==, !=, array-contains, in)を自由にミックスできます。
特に実務で重宝するのが 「array-contains」 と 「in」 オペレーターです。
例えば、タグが複数付与された記事から「’javascript’タグを含むもの」を抽出するクエリは、以下のように記述できます。
// タグ配列に'javascript'を含むドキュメントを取得
const snapshot = await db.collection('posts')
.where('tags', 'array-contains', 'javascript')
.where('status', '==', 'published')
.orderBy('createdAt', 'desc')
.limit(20)
.get();
このクエリをRealtime Databaseで再現しようとすれば、全記事を取得した上でアプリケーション側でタグ配列を走査せざるを得ません。
また、Firestoreはインデックスを自動提案してくれるため、複合インデックス未定義の状態でクエリを実行すると、エラーメッセージ内に「このクエリに必要なインデックスはこちらです」というURL付きのヒントが表示されます。
このデベロッパーエクスペリエンスの高さは、開発速度に直結する大きなアドバンテージです。
スケーラビリティと自動シャーディング-運用者が考えるべき手間を徹底比較
スケーラビリティの差は、システムが成長した後の運用者の睡眠時間に直結します。
Realtime Databaseでは、同時接続数が10万を超えるとクラスタリング構成を検討する必要があり、その場合でも書き込みスループットは1ノードあたり1,000件/秒という制約が残ります。
この制約を超えるには、アプリケーション層で複数のデータベースインスタンスにデータを分割するシャーディングロジックを自前実装しなければなりません。
ユーザーIDのハッシュ値でインスタンスを振り分けるような処理を追加すると、クエリの複雑性が一気に跳ね上がります。
Firestoreは、この問題を完全に抽象化しています。
データは自動的に複数のパーティションに分散され、書き込みスループットはほぼリニアに拡張されます。
ホットスポット(特定のコレクションやドキュメントへの集中アクセス)が発生した場合も、内部的にパーティションスプリットが行われ、運用者が介入する余地はありません。
- Realtime Database:スケールアウトには手動シャーディング必須。接続数上限10万。書き込み上限1,000件/秒
- Firestore:自動スケーリング。上限実質なし。書き込みスループットは自動拡張。運用介入ゼロ
この差は、数千ユーザー規模のスタートアップと、数百万ユーザーを抱える大規模サービスの両方で、アーキテクチャの再設計コストを劇的に変えます。
最初からFirestoreを選んでおけば、後年の苦しいリファクタリングを回避できるケースがほとんどです。
データモデリングの自由度-サブコレクションと参照設計で広がる可能性
最後に、データモデリングの柔軟性について語らねばなりません。
Realtime DatabaseがフラットなJSONツリーしか持たないのに対し、Firestoreはコレクション内のドキュメント配下にさらにサブコレクションを配置できます。
これにより、1対多の関係を自然に表現できるだけでなく、親ドキュメントとは独立したクエリをサブコレクションに対して実行することも可能です。
例えば、usersコレクションの各ユーザードキュメント配下にordersサブコレクションを持たせる設計を考えます。
このとき、特定ユーザーの注文履歴だけを取得するクエリは、親のユーザードキュメントを読み取らずに直接サブコレクションを参照できます。
さらに、コレクショングループクエリを使えば、全ユーザーにまたがる「すべての注文」を横断検索することも可能です。
- 親ドキュメントとは独立したサブコレクションのクエリ実行が可能
- コレクショングループクエリにより、複数コレクションを横断した一括検索を実現
- 参照(Reference)型フィールドを使えば、RDBMS的な外部キー類似のリレーションも設計可能
加えて、Firestoreはフィールド単位で配列、マップ(ネストされたオブジェクト)、タイムスタンプ、地理位置情報など、多様なデータ型をネイティブにサポートします。
これにより、アプリケーションのドメインモデルをそのままデータベースにマッピングできるため、ORM的な変換レイヤーが薄くなり、コードの保守性も向上します。
Realtime Databaseでは文字列や数値、真偽値、配列程度しか扱えず、地理情報やネスト構造はJSONとして文字列化して保存せざるを得ないケースが多々ありました。
以上の3ポイントは、いずれも「Firestoreはモダンなアプリケーション開発の要求に応えるためにゼロから設計された」という事実を裏付けています。
次のセクションでは、あえてRealtime Databaseを選ぶべきシナリオについて、公平な視点で考察します。
逆転のポイント?Realtime Databaseが未だに選ばれる3つのシナリオ

Firestoreが多くの点で優位性を持つとはいえ、Realtime Databaseには依然として独自の強みが存在します。
それは「超低遅延」と「シンプルさ」という、ある種のトレードオフを重視するシナリオにおいて顕著に現れます。
ここでは、あえてRealtime Databaseを選択すべき具体的な3つのケースを、パフォーマンス測定データや運用実績を交えながら検証してみましょう。
低レイテンシが絶対条件のリアルタイム同期型アプリケーション
Realtime DatabaseがFirestoreに対して最も明確に優位性を持つのは、エンドツーエンドのレイテンシです。
実測値によれば、同じリージョン内での通信において、Realtime Databaseは平均で30〜50ミリ秒、Firestoreは80〜120ミリ秒程度の応答時間を示すケースが多く報告されています。
この差は、WebSocketベースの永続接続を採用しているRealtime Databaseに対し、FirestoreがHTTP/2ストリーミング+WebSocketフォールバックという複合プロトコルを利用していることに起因します。
この数十ミリ秒の差が致命的になるのは、以下のようなドメインです。
- オンライン対戦ゲーム:プレイヤーの位置情報やアクションを100ミリ秒未満で同期する必要があるFPSや格闘ゲーム
- VoIPシグナリング:通話開始や切断のシグナルが遅れると、ユーザー体験が著しく損なわれる
- 産業機器の遠隔制御:工場のセンサーデータやロボットアームへの指令が微妙に遅延すると、安全機構に影響を及ぼす可能性がある
- ライブパフォーマンスのモニタリング:音楽や舞台の照明制御など、人間の知覚閾値に近い応答が求められるケース
これらのユースケースでは、クエリの複雑さよりも、とにかく速い書き込みと即時通知が優先されます。
Realtime Databaseはデータ構造が単純な分、通信オーバーヘッドが最小化されており、かつオフライン時にローカルで実行された書き込みがサーバー復旧後に自動的に競合解決される仕組みも、トランザクションの観点では直感的に扱いやすいと言えます。
シンプルなJSONツリーで十分な小規模プロトタイプや管理画面
もう一つの合理的な選択理由は、データ構造が最初から完全に固定されており、かつ階層が深くならない場合です。
例えば、社内用のタスク管理ツールのプロトタイプ、イベント参加者のステータス管理ボード、あるいはIoTデバイスの簡易状態監視ダッシュボードなどが該当します。
これらのシステムでは、以下の条件が揃っていることが多いです。
- データの種類が3〜5種程度で、関連性が単純(例:ユーザー、タスク、コメントのみ)
- アクセスパターンが「全件取得して表示」か「特定IDの1件更新」に限定される
- 同時接続数がせいぜい数十〜数百で、スケーラビリティがほとんど問題にならない
- 開発期間が極めて短く、学習コストを最小化したい
Realtime Databaseは、データをエクスポートするとそのままJSONファイルになるため、バックアップや手動編集が極めて容易です。
また、Firebaseコンソール上で直接ツリーを編集できるインターフェースは、エンジニア以外のメンバーでも扱いやすく、プロトタイプ段階の高速な試行錯誤に向いています。
加えて、セキュリティルールの記述がパスベースで直感的である点も見逃せません。
例えば、/users/{uid}/profile に対するアクセス制御を数行で記述でき、Firestoreのコレクション+ドキュメント+フィールド単位のルールよりも学習曲線が緩やかです。
このシンプルさは、初期フェーズのスタートアップやハッカソン的なプロジェクトにおいて、開発速度を優先する戦略と合致します。
ただし、ここで注意すべきは、プロトタイプがそのまま本番化されるケースです。
初期はシンプルだったデータ構造も、機能追加に伴ってネストが深くなり、クエリ性能が劣化していくのは多くの現場で観測される現象です。
そのため、Realtime Databaseを選ぶ際は「このシンプルさが将来にわたって維持されるか」を厳しく見極める必要があります。
次のセクションでは、両者の選択を最終判断するための実践的なマトリクスを提示します。
オフライン競合解決の直感性-トランザクション処理の差異を検証する

モバイルアプリやWebアプリにおいて、ネットワークが不安定な環境でのオフライン対応はもはや必須要件です。
FirestoreとRealtime Databaseはどちらもオフライン永続化をサポートしますが、競合が発生したときの解決戦略には本質的な違いがあります。
この差異は、特に複数ユーザーが同時に同じデータを編集する協調作業型アプリケーションにおいて、実装の複雑さとデータ整合性に直結します。
ここでは、トランザクション処理と競合解決のメカニズムを徹底比較します。
オフライン書き込みのキューイング戦略
両サービスとも、オフライン時に実行された書き込み操作はローカルキャッシュにキューイングされ、オンライン復旧時にサーバーへ送信されます。
しかし、その挙動の細部は大きく異なります。
Realtime Databaseは、オフライン中のすべての書き込みを操作単位で記録し、サーバー復旧後にその操作を送信順に再生します。
このとき、同じパスに対する複数の書き込みが競合した場合、最後に書き込まれた値が優先されるという「ラストライトウィン」ポリシーが適用されます。
例えば、ユーザーAがオフラインで/counterを10に更新し、その直後にユーザーBがオフラインで15に更新した場合、サーバー復旧後の最終値は15になります。
これは直感的で理解しやすい一方、ビジネスロジック上は不適切なケースも少なくありません。
Firestoreは、この問題に対してより洗練されたアプローチを取ります。
Firestoreのオフラインキューはドキュメント単位のスナップショット差分として管理され、復旧時にサーバーとローカルの状態をマージします。
競合が発生した場合は、サーバー上の最新バージョンが優先され、ローカルの未反映書き込みは破棄されます。
この動作は、結果整合性を重視する多くのアプリケーションには適していますが、ユーザーから見ると「オフライン中に編集した内容が消えた」という不満を招く可能性もあります。
トランザクションAPIの使い勝手比較
より複雑な競合解決が必要な場合、両者はトランザクションAPIを提供していますが、その実装哲学が異なります。
Realtime Databaseのトランザクションは、コールバックベースのリトライモデルです。
特定のパスに対してトランザクションを開始し、現在値を読み取り、新しい値を計算して書き戻す処理をアトミックに実行します。
サーバー側で競合が検出された場合、トランザクションは自動的に再実行されます。
この仕組みは、カウンターのインクリメントや在庫数の減算といった単一パスに対するアトミック操作に非常に適しています。
// Realtime Databaseのトランザクション例
const ref = db.ref('/counter');
ref.transaction((current) => {
return (current || 0) + 1;
});
Firestoreのトランザクションは、複数のドキュメントをまたがる一貫性をサポートする点で優位です。
最大500個のドキュメントに対する読み取りと書き込みを1つのトランザクションでまとめて実行でき、すべて成功するか、すべて失敗するかの原子性が保証されます。
ただし、Firestoreはオフライン状態ではトランザクションを実行できないという制約があり、オンライン時のみ有効です。
| 比較軸 | Realtime Database | Firestore |
|---|---|---|
| オフライントランザクション | 可能(キュー再生) | 不可(オンライン必須) |
| 複数ドキュメント対応 | 不可(単一パスのみ) | 可能(最大500ドキュメント) |
| 競合解決ポリシー | ラストライトウィン | サーバーバージョン優先 |
| リトライ制御 | 自動リトライ(上限あり) | アプリ側で再実行必要 |
| 学習コスト | 低い(直感的) | 中程度(エラーハンドリング要) |
実践的な選択基準-どちらの競合解決があなたに合うか
この違いを踏まえると、選択の指針は明確になります。
単一のカウンターやフラグ管理が中心で、オフラインでも書き込みを優先したいユースケース(例:歩数計アプリのスコア更新)では、Realtime Databaseのラストライトウィン方式のほうが実装が簡潔です。
一方、複数の関連データを整合性保ったまま更新したいケース(例:ショッピングカートの商品追加と在庫減算の同時実行)や、オフライン時の編集は破棄しても構わないシナリオでは、Firestoreのトランザクションが堅牢性を提供します。
また、オフライン編集の競合をアプリケーション側で細かく制御したい場合、Firestoreではドキュメントにバージョン番号やタイムスタンプを付与し、カスタムロジックで競合を検出・解決するパターンが一般的です。
これにより、最終的にユーザーに競合解決を委ねるUI(例:Googleドキュメントのような差分表示)も実装可能です。
Realtime Databaseではそのような細粒度制御が難しいため、設計初期にこのトレードオフを十分に認識しておくことが、後々の手戻りを防ぐ最大のポイントと言えるでしょう。
実プロジェクトで見る料金モデルの落とし穴-ドキュメント課金 vs 転送量課金

データベース選択において、機能やパフォーマンスと並んで軽視できないのが料金モデルです。
FirestoreとRealtime Databaseはまったく異なる課金体系を採用しており、設計の良し悪しで月間コストが数倍から数十倍にも変動します。
ここでは、実際のプロジェクトで発生しがちな落とし穴を、具体的な数値シミュレーションを交えながら解説します。
Firestoreのドキュメント課金-読み取り・書き込み・削除の3軸で考える
Firestoreの課金は、操作の種類と回数に基づきます。
具体的には、ドキュメントの読み取り(取得)、書き込み(作成・更新)、削除の各操作に対して、それぞれドキュメント単位で料金が発生します。
加えて、保存データ量(GB)とネットワーク転送量(GB)も課金対象です。
このモデルの特徴は、データのサイズよりも操作回数がコストを支配するという点にあります。
例えば、1KB未満のドキュメントを1日10万回読み取る場合と、1MBのドキュメントを1日1万回読み取る場合を比較してみましょう。
Firestoreでは読み取り回数が同じなら、ドキュメントサイズが大きくても読み取り料金は変わりません(転送料金は別途かかります)
これは一見すると公平ですが、不要なフィールドまで含めてドキュメント全体を取得する実装がコストを急増させる原因になります。
- 読み取り1回あたり:$0.06 / 10万回(米国リージョン、2024年時点)
- 書き込み1回あたり:$0.18 / 10万回
- 削除1回あたり:$0.02 / 10万回
- 保存データ:$0.18 / GB・月
- ネットワーク転送:$0.12 / GB(外向き)
ここで落とし穴となるのが、リスナー(リアルタイム更新)の動作です。
FirestoreでonSnapshotを使用してドキュメントを監視する場合、変更がなくても初期読み取りが1回カウントされ、さらに更新があるたびに読み取りが追加でカウントされます。
つまり、頻繁に更新されるドキュメントを多数リスニングしていると、想定以上の読み取り料金が発生するのです。
Realtime Databaseの転送量課金-データサイズと帯域が直接コストに
一方、Realtime Databaseの課金は転送データ量(ダウンロード)と保存データ量が中心です。
操作回数に対する直接課金はなく、代わりに送受信されるバイト数に応じて料金が発生します。
このモデルは、1回の操作で大量のデータを取得する場合に不利に働く一方、小さいデータを頻繁にやり取りするユースケースではコストが抑えられます。
- ダウンロード転送:$1.00 / GB(米国リージョン)
- 保存データ:$5.00 / GB・月
- アップロード転送:無料
ここで典型的な落とし穴は、深くネストされたデータをルート近くで取得するケースです。
例えば、/users/{uid}/ を参照すると、そのユーザーのプロフィール、設定、投稿履歴、メッセージ履歴など、子ノードすべてが一度にダウンロードされます。
アプリ側で必要なフィールドだけを選択的に取得する手段がないため、データ構造が大きくなるほど転送量が爆発的に増加します。
実際のプロジェクトでのコストシミュレーション
具体的な例として、1日あたり10万アクティブユーザーが存在するSNSアプリを想定します。
各ユーザーが1日にプロフィールを5回読み取り、タイムライン(20件の投稿を含むドキュメント)を10回読み取り、さらに週に1回投稿するというモデルです。
Firestoreの場合:
- プロフィール読み取り:10万ユーザー × 5回 = 50万読み取り/日 → 月間1,500万読み取り → $9.00/月
- タイムライン読み取り:10万ユーザー × 10回 × 20投稿 = 2,000万読み取り/日 → 月間6億読み取り → $360.00/月
- 投稿書き込み:10万ユーザー × 1/7回 = 約1.4万書き込み/日 → 月間42万書き込み → $0.76/月
- 保存データ:仮に50GB → $9.00/月
- 合計:約$379/月
Realtime Databaseの場合:
- プロフィール(仮に5KB)読み取り:50万回/日 × 5KB = 2.5GB/日 → 75GB/月 → $75/月
- タイムライン(仮に20KB)読み取り:100万回/日 × 20KB = 20GB/日 → 600GB/月 → $600/月
- 投稿書き込み(アップロード無料)→ $0
- 保存データ(仮に50GB)→ $250/月
- 合計:約$925/月
このシミュレーションから、読み取り回数が多いが各データが小さいFirestoreは、転送量が多いRealtime Databaseよりもコスト優位になる傾向が見て取れます。
ただし、Firestoreで同じタイムラインを実現するには、投稿を個別ドキュメントとして別コレクションに格納し、ページネーションクエリで取得する設計が必須です。
もしタイムライン全体を1つの巨大ドキュメントに格納してしまうと、読み取り回数は減りますが、ドキュメントサイズが大きくなり保存コストと転送コストが増加します。
コスト最適化のための実践的アドバイス
最終的に、料金モデルだけで判断するのは危険です。
Firestoreは操作回数を減らす設計(例:バッチ読み取り、ページネーション、不要フィールドの非取得)が、Realtime Databaseは転送量を減らす設計(例:浅いパス設計、データの正規化による重複排除)がそれぞれ求められます。
また、両サービスとも無料枠(Firestoreは1日5万読み取り、2万書き込み、2万削除まで無料。
Realtime Databaseは同時接続100、ダウンロード10GB/月まで無料)を活用し、プロトタイプ段階ではコストをほぼゼロに抑えられる点も見逃せません。
プロジェクトの成長フェーズに合わせて、定期的に使用状況をモニタリングし、インデックスやクエリの見直しを行うことが、予期せぬ高額請求を防ぐ唯一の方法です。
Firestore移行時に注意すべき設計パターン-正規化と非正規化のバランス

既存のRealtime DatabaseプロジェクトをFirestoreへ移行する、あるいはRDBMSからFirestoreへ乗り換える際、最も頭を悩ませるのがデータモデルの再設計です。
FirestoreはNoSQLドキュメントデータベースであり、リレーショナルデータベースで常識とされてきた正規化の原則がそのまま通用しません。
ここでは、移行時に特に注意すべき設計パターンと、正規化と非正規化の最適なバランスについて実践的な指針を提示します。
リレーショナル思考の呪縛から脱却する
Firestoreに慣れていない開発者が最初に陥る過ちは、RDBMSのスキーマ設計をそのままコレクション構造にマッピングしようとすることです。
例えば、usersテーブルとordersテーブルを外部キーで結合する設計を、そのままusersコレクションとordersコレクションに分割し、クライアント側で2回クエリを実行して結合するような実装です。
これはFirestoreのパフォーマンスとコストの両面で非効率であり、むしろ非正規化(データの重複を許容して一括取得を可能にする)を積極的に検討すべきケースがほとんどです。
- RDBMSの発想:正規化して冗長性を排除。JOINで必要なデータを都度結合
- Firestoreの発想:読み取り時に必要なデータはすべて1つのドキュメントに格納。書き込み時に複数箇所を更新(デノーマライゼーション)
この考え方の転換が最も顕著に現れるのが、1対多や多対多のリレーションシップです。
RDBMSでは中間テーブルを用いて正規化しますが、Firestoreでは以下のいずれかのパターンを選択します。
- サブコレクション:親ドキュメント配下に子コレクションを配置。例:
users/{uid}/orders/{orderId}。親と子を独立してクエリ可能 - 配列フィールド:親ドキュメント内に子のIDや参照を配列で保持。例:
user.postIds = ['post1', 'post2'] - マップ(オブジェクト)埋め込み:関係性が固定かつ少量の場合、子データを直接親ドキュメントのフィールドとして埋め込む
非正規化がもたらす利点と代償
非正規化の最大の利点は、1回の読み取りで必要な全データを取得できることです。
例えば、ブログ記事の一覧画面で「記事タイトル+著者名+著者プロフィール画像」を表示するケースを考えます。
RDBMSならarticlesテーブルとusersテーブルをJOINする必要がありますが、Firestoreではarticlesコレクションの各ドキュメントにauthorNameとauthorAvatarフィールドを直接持たせてしまえば、記事一覧を取得するだけで情報が揃います。
ただし、このアプローチには明確な代償があります。
データの更新が複数箇所に波及するのです。
著者がプロフィール画像を変更した場合、その著者が執筆したすべての記事ドキュメントを検索して画像フィールドを更新しなければなりません。
この更新処理を忘れると、表示上の不整合が発生します。
このトレードオフを管理するための実践的な戦略は、読み取り頻度と書き込み頻度のバランスで判断することです。
- 読み取り頻度が極めて高く、書き込みが稀なデータ(例:記事一覧の著者名)→ 積極的に非正規化する
- 書き込み頻度が高く、読み取りはそれほど多くないデータ(例:ユーザーのログインステータス)→ 参照(Reference)型を使って都度取得する
- 読み取り・書き込みともに中程度 → サブコレクション+コレクショングループクエリを活用し、必要な時にだけ子データを取得する
バッチ書き込みとトランザクションを活用した整合性維持
非正規化したデータの整合性を保つには、バッチ書き込みやトランザクションを積極的に利用します。
Firestoreのバッチ書き込みは、複数のドキュメントに対する書き込み操作を1つのアトミックな単位として実行できるため、親ドキュメントと子ドキュメントの同時更新や、複数コレクションにまたがる非正規化フィールドの一括更新に適しています。
// バッチ書き込みの例:著者情報更新を全記事に反映
const batch = db.batch();
const authorRef = db.collection('users').doc(authorId);
const articlesSnapshot = await db.collection('articles')
.where('authorId', '==', authorId)
.get();
articlesSnapshot.forEach((doc) => {
batch.update(doc.ref, { authorName: newName, authorAvatar: newAvatar });
});
batch.update(authorRef, { name: newName, avatar: newAvatar });
await batch.commit();
このコードは、著者情報の更新をすべての関連記事に反映させると同時に、著者ドキュメント自体も更新します。
バッチは最大500件の操作までをまとめられるため、対象記事がそれ以上ある場合は複数バッチに分割するか、Cloud Functionsを用いた非同期バックグラウンド処理を検討します。
移行時に推奨される段階的アプローチ
移行プロジェクトでは、すべてを一度に非正規化しようとしないことが成功の鍵です。
まずは読み取りパターンを洗い出し、クリティカルな画面(例:トップページ、プロフィール画面)から優先的に非正規化設計を適用します。
その上で、書き込み時に更新漏れが起きやすいフィールドにはCloud Functionsトリガーを設定し、親ドキュメントの更新をフックに子ドキュメント群を自動更新する仕組みを構築するのが堅牢です。
最終的に、正規化と非正規化は二択ではなくスペクトラムであることを理解してください。
コレクショングループクエリや参照型フィールドを適宜組み合わせることで、更新コストと読み取りコストをトレードオフさせながら、アプリケーションの要求に合わせた最適な設計を段階的に実現することが、Firestore移行を成功させる本質的なアプローチと言えます。
ユースケース別最適選択マトリクス-これで迷わない実践的な判断基準

ここまで、アーキテクチャ、クエリ性能、スケーラビリティ、オフライン処理、料金モデル、設計パターンと、両データベースの特徴を多角的に比較してきました。
しかし、実際のプロジェクトでどちらを選ぶかは、機能の優劣ではなく、あなたのアプリケーションが置かれたコンテキストで決まるべきです。
そこで本セクションでは、具体的なユースケースごとに最適な選択を提示する判断マトリクスを構築します。
このマトリクスを意思決定の軸として活用してください。
判断軸を明確にする-5つの評価ディメンション
最適選択を導くために、以下の5つの評価ディメンションを定義します。
各ディメンションについて、自プロジェクトがどの程度の重要度を持つかを5段階(1:低〜5:高)でスコアリングすると、客観的な判断が可能になります。
- クエリ複雑性:複数条件でのフィルタリングやソート、範囲指定、集計処理が必要かどうか
- データボリューム:将来的なドキュメント数や総データサイズの見込み(数百件レベルか、数億件レベルか)
- 同時接続数:ピーク時のアクティブユーザーまたはデバイス接続数
- レイテンシ要件:許容される応答遅延(ミリ秒単位の厳格さが必要か、数百ミリ秒まで許容できるか)
- データ構造の変更容易性:リリース後にスキーマ変更やフィールド追加が頻繁に発生する見込みか
これらのスコアを基に、以下のマトリクスを参照します。
各ユースケースは典型的なスコアパターンと推奨DBを示しています。
| ユースケース | クエリ複雑性 | データボリューム | 同時接続 | レイテンシ要件 | 構造変更頻度 | 推奨DB |
|---|---|---|---|---|---|---|
| ソーシャルフィード(タイムライン) | 高(5) | 高(5) | 高(4) | 中(3) | 高(4) | Firestore |
| Eコマース商品カタログ | 高(5) | 高(5) | 高(5) | 中(3) | 中(3) | Firestore |
| チャット/メッセージング | 低(2) | 高(5) | 高(5) | 高(5) | 低(2) | Realtime DB |
| オンラインゲームスコアボード | 低(2) | 中(3) | 高(4) | 高(5) | 低(1) | Realtime DB |
| 管理ダッシュボード(社内用) | 中(3) | 低(2) | 低(2) | 低(2) | 高(4) | どちらも可 |
| IoTデバイス状態監視 | 低(2) | 高(4) | 高(5) | 高(5) | 低(2) | Realtime DB |
| ユーザープロフィール管理 | 中(3) | 高(4) | 高(4) | 中(3) | 中(3) | Firestore |
| リアルタイム協調編集(ドキュメント) | 高(4) | 中(3) | 中(3) | 高(5) | 中(3) | Firestore(+OT/CRDT別途) |
スコアリングによる実践的評価プロセス
実際のプロジェクトでこのマトリクスを活用するには、まず各ディメンションに重み付けを行います。
例えば、スタートアップのMVP開発では「構造変更頻度」と「開発速度」が最優先され、大企業のバックオフィスシステムでは「データボリューム」と「同時接続」が重視されるでしょう。
具体的な判断フローは以下の通りです。
- クエリ複雑性が4以上の場合 → Firestoreを第一候補にする。Realtime Databaseでは実装困難またはクライアント処理が過大になるリスクが高い
- レイテンシ要件が5(100ミリ秒未満が必須)かつデータ構造が単純(JSON階層が3階層以内)の場合 → Realtime Databaseを検討する
- データボリュームが5かつ同時接続が4以上の場合 → Firestoreの自動スケーリングが大きなアドバンテージになる
- 構造変更頻度が4以上の場合 → Firestoreのスキーマレス設計が変更コストを大幅に削減する
- 上記のどれにも該当しないミドルケースでは、エコシステムと将来の拡張性で判断する。FirestoreはFirebaseエコシステム全体(Cloud Functions、Authentication、Storage)との連携がよりシームレスである
ハイブリッド運用という現実解
最後に、あえて両方を併用するハイブリッドアーキテクチャも選択肢として提示します。
例えば、メインのユーザーデータやコンテンツはFirestoreで管理し、チャット機能やアクティビティ通知のような超低遅延が求められる一部モジュールだけをRealtime Databaseで実装する構成です。
この場合、FirestoreのセキュリティルールとRealtime Databaseのセキュリティルールを別個に管理する手間が増えますが、それぞれの強みを活かせる最適解になるケースも少なくありません。
結論として、「最新だからFirestore」ではなく、「要件に合わせて選ぶ」 という姿勢が何より重要です。
本マトリクスを意思決定の出発点とし、実際に小規模なプロトタイプを両方で実装して性能を計測することを強く推奨します。
その上で、運用開始後のモニタリングと見直しを定期的に行うサイクルを構築できれば、データベース選択がボトルネックになることは決してないでしょう。
まとめ:将来を見据えた選択とは?Firestoreが持つエコシステム的優位性

ここまで約7,000文字にわたり、FirestoreとRealtime Databaseの差異をアーキテクチャから料金モデル、設計パターン、ユースケース別判断基準まで多角的に検証してきました。
最終章では、これらの知見を総合的に統合し、将来を見据えた選択という観点から、Firestoreが持つエコシステム的な優位性を改めて明確化します。
単なる機能比較を超えた、戦略的な意思決定のための視点を提供しましょう。
単独のデータベースではなく「プラットフォーム」としてのFirebase
Firestoreを評価する際にしばしば見落とされがちなのが、Firebaseエコシステム全体との統合度です。
FirestoreはCloud Functions、Cloud Storage、Authentication、Cloud Messaging、Analyticsといった他のFirebaseサービスとネイティブに連携するよう設計されています。
例えば、Cloud FunctionsのトリガーはFirestoreのドキュメント作成・更新・削除をフックにでき、Authenticationのユーザー作成イベントと連動してプロフィールドキュメントを自動生成することも数行のコードで実現します。
Realtime Databaseもこれらのサービスと連携可能ですが、その接続はやや「後付け」の印象があり、特にセキュリティルールの記述やトリガー設定の柔軟性でFirestoreに軍配が上がります。
FirestoreはFirebaseがモバイル・Webアプリケーションのバックエンドとして提供する統合プラットフォームの中心的存在であり、単独のデータベースではなく、プラットフォーム全体の進化に合わせて継続的に機能拡充がなされています。
継続的なアップデートとロードマップの透明性
もう一つの重要な要素は、Googleによる開発リソースの配分です。
Firestoreは2017年のベータリリース以降、コレクショングループクエリ、フィールドマスクによる部分読み取り、新しいオペレーター(!=、not-in)、エクスポート/インポート機能の強化など、実に多くの機能が継続的に追加されてきました。
一方、Realtime Databaseは2012年のリリース以降、大きな機能アップデートはほとんどなく、現状はメンテナンスモードに近い状態と言わざるを得ません。
公式発表こそありませんが、エンジニアリングコミュニティでは「Realtime Databaseはレガシー製品であり、新機能はすべてFirestoreに集中している」という見方がコンセンサスとなっています。
つまり、今後5年間で必要になるであろう新機能やパフォーマンス改善は、ほぼ確実にFirestoreでしか提供されないと見てよいでしょう。
コミュニティとドキュメントの充実度
エコシステムの質は、ドキュメントの豊富さとコミュニティの活発さにも現れます。
FirestoreはStack Overflowの質問数がRealtime Databaseの約3倍に達し、GitHub上のサンプルコードやオープンソースライブラリも圧倒的に豊富です。
特に、ReactやVue.js、Flutterなどのモダンフレームワークとの連携例はFirestoreを前提としたものが主流になっており、採用する際の学習リソースの確保が容易です。
また、公式ドキュメントには「ベストプラクティス」「料金最適化ガイド」「セキュリティルールの実践パターン」などが体系的に整理されており、開発者が躓きやすいポイントを事前に回避できる設計がなされています。
この情報の非対称性は、開発速度と品質担保に直接影響するため、チームの生産性を重視するならFirestoreが明確に有利です。
最終判断:どのようなプロジェクトにFirestoreを選ぶべきか
本記事の総括として、以下の条件に1つでも該当するプロジェクトは、迷わずFirestoreを選択することを推奨します。
- 将来的にデータ構造が変化する可能性がある(ほとんどすべてのプロジェクトに該当)
- 複雑な検索機能やフィルタリングUIを実装する予定がある
- 同時接続数が1,000を超える見込みがある
- Google Cloud Platform(GCP)の他のサービス(BigQuery、Pub/Sub、Vertex AIなど)との連携を視野に入れている
- 開発チームのスキルセットがモダンなフレームワーク(React、Next.js、Flutter、SwiftUIなど)に依存している
逆に、以下の非常に限定的な条件がすべて揃う場合のみ、Realtime Databaseを選択肢として残してください。
- データ構造がリリース前から完全に固定されており、かつ階層が3階層以内
- エンドツーエンドのレイテンシが100ミリ秒未満であることがビジネス上必須
- 同時接続数が常に500未満であり、スケーラビリティが課題にならない
- 開発期間が2週間未満で、学習コストを極限まで削減する必要がある
最後に-選択は未来への投資である
データベース選定は、単なる技術選定ではなく、将来の運用コストと開発生産性に対する先行投資です。
Firestoreはその投資対効果が非常に高く、特にスタートアップや新規プロジェクトにおいては、初期の学習コストを上回るリターンを中長期的にもたらすでしょう。
Realtime Databaseのシンプルさに魅かれる気持ちは理解できますが、拡張性とエコシステムの将来性を天秤にかければ、その選択が後悔に変わる確率は極めて高いと言わざるを得ません。
本記事があなたのプロジェクトにおける最適な判断の一助となれば幸いです。
どちらのデータベースを選んだとしても、その選択を定期的に見直し、サービスの成長に合わせてアーキテクチャを進化させていく姿勢が、エンジニアとしての真の力量を問われるポイントだと、私は確信しています。


コメント