FirestoreとRealtime Databaseとわたし(たち)

この記事は Goodpatch Advent Calendar 2020 7日目の記事です!

はじめに

Googleが提供するFirebaseには、FirestoreとRealtime Databaseの2つのデータベースが存在します。私が所属するStrapチームでは、これらのデータベースを活用してサービス開発を行っています。
今回は、この2つのデータベースの違いと、その特徴から弊チームで考えた使い分けについてご紹介します。

FirestoreとRealtime Database

Wikipediaによると、Firebaseはもともと2011年に設立され、前身の企業で利用していたチャットシステムに利用されていたリアルタイムアーキテクチャを元に開発された、Realtime Databaseを提供する企業でした。
その後、HostingやAuthenticationなどの機能の追加を経てmBaaSとなり、2014年にGoogleに買収されました。
そしてGoogleの買収後の2017年に登場した、新たな後継データベースがFirestoreです。

データベースの特徴と比較

Realtime DatabaseとFirestoreの特徴を公式サイトを元に確認してみましょう。

データベースの選択: Cloud Firestore または Realtime Database | Firebase

Realtime DatabaseとFirestore

両者には様々な違いがありますが、アプリケーションを構築する上での大きな違いとしては、データモデルや、クエリの柔軟性 が挙げられます。

また、課金体系にも違いがあり、Realtime Databaseは下りのデータ量保存量に対して課金されるのに対して、Firestoreはそれらに加えてデータベースオペレーションに対しても課金されます。
一見Firestoreのほうが高コストのように見えますが、課金レートはRealtime Databaseに比べて低く設定されているようです。

Understand Realtime Database Billing | Firebase Realtime Database
– Understand Cloud Firestore billing | Firebase

結論: まずはFirestoreで検討する

さっそくの結論ですが、2020年現在、公式ドキュメントにも記載があるとおり、基本的にはFirestoreを利用することがおすすめされます。

先の比較の通り、Firestoreではデータ構造の作りやすさや柔軟なクエリのサポートなど、Realtime Databaseと比較して有利な点が数多くあります。

ある程度の規模で提供するアプリケーションであれば、Security Ruleで提供される機能の多さからも、Firestoreのほうが、より複雑な要件にも対応しやすいはずです。

Realtime Databaseの使い所

では、どんなときにRealtime Databaseを利用する場面があるでしょうか。

Realtime DatabaseはFirestoreに比べてレイテンシが優れていて、Firestoreとは課金体型が異なり、Realtime Databaseでは下りの帯域と保存容量に対して課金されます。

このようなことから、よりリアルタイム性が求められる要件や、読み取りに対して書き込みの頻度が多い場面では、Realtime Databaseが選択肢として挙がりそうです。

公式ドキュメントでは、ユーザーのオンライン状態を表示するようなプレゼンスの機能を例にとって、Realtime DatabaseとFirestoreの併用を提案しています。

Realtime Databaseにはユーザーのオンライン状態を自動的に書き込む独自のパスが存在し、これを利用したソリューションです。

Cloud Firestore でプレゼンスを構築する | Firebase

ロールベースアクセス制御する場合は注意

Realtime DatabaseをFirestoreと併用する上では注意点もあります。
アプリケーションで、ある程度複雑なロールベースのアクセス制御を行っている場合には、特に注意が必要です。

具体的な事例を挙げてみます。

Firestore上でロールベースのアクセス制御を行い、かつFirestoreにロール情報を保存し、FirestoreのSecurity Ruleで参照し制御しているような場合には、そのFirestoreのロール情報をRealtime Databaseは参照することができません。

例えば、ロールや所属するグループによって公開する情報を厳格に制御したい場合には、Realtime Databaseに保存する情報はIDなどの最低限の情報にするなどの対策が必要になるでしょう。

Realtime Database – Firestoreの併用

先のプレゼンスを例に取ると、特定のロールやグループにだけプレゼンスを提供するには、Firebase Authenticationの提供するCustom Claimsとの連携が必要になります。

しかし、Custom Claimsには1ユーザーあたり1KBのサイズ制限が存在するため、構築しようとするアプリケーションによっては、その設計と運用を慎重に検討する必要があるでしょう。

Custom Claimsを利用した認可情報の共有


Secure data access for users and groups | Firebase

こちらの制限は、Cloud Storageと連携する場合にも当てはまります。

チーム内での運用

私が所属するStrapチームでは、FirebaseやGCPを利用したサービスを開発しています。

サービス内でFirestoreとRealtime Databaseを適材適所で活用できるように、弊チームでは、これまで紹介したような特徴や課題を踏まえて、次のようなルールを設定しています。

1. 永続化、厳格なアクセス制御が必要なデータを含め基本的にはFirestoreを利用
2. 揮発データ、リアルタイム性が求められる機能、書き込み頻度が高い機能の場合はRealtime Databaseを検討
3. チームメンバー間での共有が不要なデータ、揮発データ、秘匿性の低いデータについてはlocal storageを検討する

具体的には、ユーザーのプレゼンス表示やカーソル表示にはRealtime Databaseを活用しています。

実装例: カーソル

Strapで提供するチームメンバーのカーソル表示は、これらのルールに沿ってFirestoreとRealtime Databaseを併用して実現しています。

カーソルの位置情報は、他ユーザーにリアルタイムに反映するため頻度高く書き込む必要がありますが、データの保存は利用中のみに限られるため、先のルールに照らし合わせてRealtime Databaseでの実現が妥当だと判断しています。

実際には、同時利用中のユーザー数による書き込みの制限や、無操作時のプレゼンスとの連動など細かな制御も行っていますが、比較的低コストでの運用が実現できています。

まとめ

StrapチームにおけるFirestoreとRealtime Databaseの活用についてご紹介しました。

現代においては、Firebaseでデータベースを利用する場合、Firestoreが第一の選択肢となることは間違いありませんが、Realtime Databaseの特徴についても知っておくことで、低コストで最適な体験が実現できることもあります。

Strapにおける技術選定や技術スタックについては、こちらの記事が参考になるかもしれません。

組織でナレッジを共有できる新プロダクト「Strap」 その開発技術に「TypeScript」「Firebase」「PixiJS」「React」を選んだ理由

最後に完全なるプロモーションですが、今回紹介したStrapについてはこちらのサービスページから詳細をご確認くださいませ!

Flutter SDK v1.17でPWAを作る

先日、Flutterの最新のWebサポートの紹介において、PWA(Progressive Web Apps)サポートが紹介されていました。
https://medium.com/flutter/flutter-web-support-updates-8b14bfe6a908

PWAサポートはまだ開発中の段階のため、今後も改善や修正が入っていくことと思いますが、現在でも既存のFlutter Webアプリに少し手を入れるだけでPWAサポートを追加することが出来ました。

というわけで、試してみた結果を残しておきます。

manifest.jsonの追加とindex.html の編集

この記事を書いている時点の最新版SDK v1.17でWebサポートを有効にした状態の flutter create コマンドを実行すると、web/ディレクトリにmanifest.jsonファイルが生成されるようになっています。
{
    "name": "pwa_web_app",
    "short_name": "pwa_web_app",
    "start_url": ".",
    "display": "minimal-ui",
    "background_color": "#0175C2",
    "theme_color": "#0175C2",
    "description": "A new Flutter project.",
    "orientation": "portrait-primary",
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "icons/Icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}
また、index.htmlファイルの内容も次のように変化しています。
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="pwa_web_app">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="shortcut icon" type="image/png" href="favicon.png"/>

  <title>pwa_web_app</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <!-- This script installs service_worker.js to provide PWA functionality to
       application. For more information, see:
       https://developers.google.com/web/fundamentals/primers/service-workers -->
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('flutter_service_worker.js');
      });
    }
  </script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
この状態で flutter build webを使ってビルドを行えば、PWAの基本的なサポートが入った状態のoutputが得られます。
詳細な情報については、 https://web.dev/progressive-web-apps/#installable から確認が可能です。

既存のアプリに適用する

既存のアプリに適用するには、上記のmanifest.json, index.htmlの2ファイルをweb/フォルダに配置します。
また、最新版のSDKでビルドができるよう、flutter pub upgrade を使った依存packageの更新も必要になるかもしれません。

もしもindex.htmlを編集している場合は、manifest参照用のmetaタグと、ServiceWorker用のscriptダグを追記しましょう。

また各種サイズのアプリアイコンも新たに用意する必要があるかもしれません。
PWAとしては、最低限192×192、512×512サイズのアイコンを用意する必要があります。

以上の対応を行うことで単純なアプリであればPWA対応のbuildファイルが書き出されているはずです。

まとめ

  • flutter createで作成されるwebのtemplateがPWAをサポートした
  • 既存のFlutter webアプリでもtemplateから必要なファイルを適用することでPWA化し始めることができる
対応したアプリケーションはこちらです。
(本記事とは逸れますが、CanvasKitを利用する設定でビルドしています。)

https://ymegane.github.io/TapTapTap2/#/