見出し画像

Firestore を選ぶ前に RDB を採用できないかまず考える

はじめに

その手軽さと豊富な機能から、スタートアップを中心としたプロダクトで Firebase が採用されているのをよく見るようになりました。一連のプロダクト郡の中でも Firestore は、インスタンス管理のコスト削減、リアルタイム同期などの便利機能、公式からの各種言語別の SDK 提供といったメリットから、開発者にとって非常に使いやすいデータベースとして評価されています。

気軽に採用しがちな Firestore ですが、Firestore は決して難易度の低いデータベースではありません。特有の制約や柔軟性に欠ける部分があるため、プロジェクトの要件によっては伝統的なリレーショナルデータベース (RDB) を採用するより遥かにチャレンジングな選択肢となりうる場合があります。最近では、Firestore 特有の制約から、PostgreSQL 等の RDB に移行するケースも見かけます。

本記事では、Firestore をプロジェクトに採用する場合に "覚悟" しておくべきリスクや制約をまとめました。ここに挙げた問題は、実際に筆者が Firestore を採用して直面した問題でもあります。本記事が、自身のプロジェクトの性質を見直し、Firestore を採用するのか、RDB ではだめなのか、改めて考えるためのきっかけとなれば幸いです。

複雑なセキュリティルールの運用

Firestore では、データのアクセス制御を行うためにセキュリティルールと呼ばれる仕組みが提供されています。これにより、認証されたユーザーのみが特定のデータにアクセスできるように設定したりすることができます。
このセキュリティルールの運用が厄介です。
データが複雑になればなるほどルールの記述は複雑化し、メンテナンスの難易度は上がっていきます。
ルールのテストは必須です。エミュレータをセットアップし、想定通りのアクセス制御が行われているかしっかりテストしましょう。

複雑なセキュリティルールの運用に耐えられなくなれば、クライアントサイドからの Firestore へのアクセスを廃止し、Cloud Run や Cloud Functions などサーバーサイドからのアクセスに絞った上で、セキュリティルールを「すべて拒否」に変更するといった対応を取ることにもなるでしょう。

クエリに対する課金

Firestore の課金システムには十分注意が必要です。Firestore では、読み込み、書き込み、削除のクエリに対して課金が発生します。個人開発で、限られた人数しか使わないようなアプリケーションであれば誤差程度の金額ですが、大量のクエリが発生するプロジェクトにおいては、Firestore だけで月に 100 万円以上のコストが発生することもあります。
Firestore を利用しながらクエリのコストを抑えるには、Firestore を Cloud Run や Cloud Functions などサーバーサイドから叩くように変更した上で、前段に CDN を配置するといった対応が必要になるでしょう。

バックアップ

Firestore には、Cloud SQL に備わっているような フルマネージドなバックアップの仕組みがありません。
データのエクスポート / インポートのための API は最近用意されたので、この API と Cloud Schedular, Cloud Run (or Functions) などを組み合わせ、自前でバックアップのワークフローを構築・運用することになります。
ワークフローを用意しない場合は、最悪捨てても困らないデータだけ Firestore に格納するようにしましょう。

貧弱なクエリ

Firestore のクエリは貧弱です。少しずつ新たなクエリのサポートは進んでいますが、RDB でサポートされているような複雑なクエリは実現できないものと覚悟しましょう。もちろん、クエリがシンプルであることはメンテナビリティを考えると決して悪いことではありません。プロジェクトの要件に応じて、慎重に検討する必要があります。

最近になってようやく集計クエリがサポートされ、クエリごとのドキュメント数を取得できるようになりました。これにより、一般的なページネーションに対応したテーブル UI を実現できるようになりました。

ただし、テーブル UI における検索や「並び替え」の実現については注意が必要です。Firestore では、複数カラムによる検索を実現するためにインデックスをまず作成する必要があります。一般的な検索画面にあるようなソートを実現しようとすると、検索対象のカラム数とソート可能なカラム数の組み合わせで、膨大な数のインデックスを作成することにもなります。
作成可能なインデックス数には上限があり、数が増えてくればコード管理の必要性も出てくるでしょう。

このように、Firestore のクエリの制約により実現できない要件が発生するリスクに十分に注意しましょう。

BigQuery への同期

分析用途でも、Firestore のクエリは貧弱すぎます。そのため、格納したデータに基づいて高度な分析を行いたい場合には、BigQuery にデータを同期する必要があります。
BigQuery にデータを同期するための Extension が用意されています。基本的にはこの Extension を使って、データパイプラインを構築・運用することになります。
同期の際、クエリごとの課金が発生することにも注意が必要です。

スキーマレス DB におけるデータ完全性の確保

Firestore は NoSQL データベースであり、スキーマレスという特徴があります。スキーマレス DB はデータ構造が固定されていないため、柔軟なデータモデリングが可能であり、これによってパフォーマンスの向上や開発の迅速化が期待できるという一面があります。しかしその反面、データの完全性を確保することは難しくなります。

RDB では、スキーマがデータの構造を厳密に定義しているため、アプリケーションにバグがあっても、スキーマがデータの完全性を保つ最後の砦となってくれます。しかし、Firestore のようなスキーマレス DB では、そのような保護機能が存在しないため、想定外のデータや null が平気で DB に入り込んできます。

開発者は誰もがバグを生む可能性があり、その結果としてデータの不整合が発生することがあります。スキーマレス DB を採用する場合は、以下のような対策が求められます。

  1. バリデーションの徹底
    スキーマレス DB ではアプリケーションコードが最後の砦です。バリデーションのコードには特に注意し、綿密なコードレビューやテストでバグを防ぎましょう。

  2. マイグレーションの徹底
    アプリケーションのバグによって不正なデータが保存されてしまった場合や、既存のデータ構造を変更する場合には、既存データのマイグレーションを必ず実施し、DB の治安を回復させましょう。

おわりに

今後の新しいプロジェクトでは、基本的に PostgreSQL を選んでおけば無難かなという印象があります。
Firebase Alternative であるところの Supabase では、DB に PostgreSQL が採用されています。
Google Cloud では、Alloy DB という PostgreSQL 互換のつよつよ DB がリリースされました。また、独自路線の Cloud Spanner では、PostgreSQL インターフェイスがサポートされました。

RDB を選択しない理由として、サーバーサイドのコードを書けるエンジニアがいないという理由もあるかもしれません。しかしながら、TypeScript 界隈では、Prisma などの DB まわりのエコシステムも充実しつつあります。「TypeScript」を武器に、従来のフロントエンドエンジニアがフルスタックにコードを書ける環境が整ってきています。

以上のような技術環境の変化も見極めながら、自身のプロジェクトで Firestore を採用すべきか、あるいは伝統的な RDB を採用すべきか、改めて検討してみてください。

参考

この記事が気に入ったらサポートをしてみませんか?