Firebaseデータベース設計のコツは?

記事
IT・テクノロジー

Firebaseデータベース設計のコツは?

Firebaseのデータベースは一般的によく利用されているデータベースのタイプとは違います。SQLなどのデータベースはリレーショナル型と呼ばれる物で、複数のテーブルを関連付けて使います。 ここでは、リレーショナル型データベースの詳しい話は省きますが、Firebaseの場合はCloud Firestoreは、JSONベースのデータベースでテーブルと似たような活用法もかの運ですが、全てのドキュメントのフィールドデータが同じである必要はありません。
従って、活用の仕方も変わってきますし、向いているアプリと、向いていないアプリも当然存在します。

Firebaseのデータベースが向いているアプリ

Firebaseのデータベースは、複雑なデータ処理が要求されるようなアプリには余り向いていません。もう少し具体的にいえば、複数の関連するテーブルを使った複雑なクエリーでデータを検索したりする場合です。
こうした処理はリレーショナル型のデータベースが得意としている処理で、正規化された複数のテーブルを使ったクエリを使って必要な情報を取り出したりしたい場合には便利です。
Firebaseの場合は複数のコレクションからデータを抜き出したい場合には、複数のクエリーを使う必要がある場合がどうしても多くなります。これは、FirebaseのデータベースをアクセスするためのAPIの仕様が複数のコレクションに同時にアクセスする機能はサポートしていないので、一つ一つのコレクションから必要なデータを抜き出して利用する必要があるからです。
一つのテーブルで解決できるような情報はFirebaseでも問題なく扱うことができますし、同じコレクションに全く違う形(異なるフィールドの組み合わせを持つドキュメント)を入れて使いたい場合などにも便利です。
複数のコレクションを組み合わせて使う場合には複数のクエリーを使えば一応対応は可能です。

データベースというよりは、検索可能なデータ置き場

考え方としては、データベースというよりは、啓作可能なデータ置き場と考えた方が理解しやすいかと思います。 Microsoft Excelのテーブル(book)のように使う場合もその一つです。 以前書いた記事に、Reactで利用されるReduxの少し違った形でのアプリのデータの管理というイメージの方が近いのかもしれません。
大きな特長の一つは、一般的なデータベースに比べると高速のデータアクセスが可能で、よくサンプルアプリなどで使われているのが、チャット(メッセージング)アプリのような使い方をしても、基本的にリアルタイムでのデータの更新が可能になっています。

Firebase独特のデータ構造の設計方法

Firebaseのデータベース(Cloud Firestore)を利用する場合は独特のデータ構造の設計を行うことができます。 これは、セキュリティルールを考慮したデータ構造を採用するという方法です。
一般的なSQLのデータベースをWebサービスで利用する場合は、Webブラウザ(フロントエンド)のプログラムが直接データベースをアクセスするような作り方は通常は行いません。(技術的には可能です) 通常は、Webブラウザ(フロントエンド)は、バックエンドを介してデータベースをアクセスするように作るのが一般的な作り方です。よく利用されているWordPressなどの場合は、Webブラウザ(フロントエンド)は、PHPで書かれているバックエンドのプログラムを介してデータベースとのやり取りを行います。
このような実装をすることで、フロントエンドには必要最低限のデータのみを渡すように設計されます。
Firebaseの場合は、実際のデータはバックエンドにありますが、ほぼデータベースの全てのデータにアクセスが可能です。そのままでは、データーのアクセス権限を管理できないのでセキュリティルールを導入して個々のデータアクセスを制限できるように設計されています。
一つの例として、利用者の権限毎にアクセスする権限を変える場合は、権限毎にコレクションを分けるとセキュリティルールの設定がシンプルになります。
例えば複数の利用者が利用可能な住所録のデータベースを作ることを考えてみます。
Firebaseでこうしたサービスを実装する場合の方法として以下の2つの方法を考えてみます。
* 一つのコレクション「address_book」に全てのデータを入れる
* 「address_book」の下に書く利用者ごとの別のサブコレクションを作って利用者ごとにデータを分ける

一つのコレクション「address_book」に全てのデータを入れる

全ての利用者のデータを一括して一つのコレクションに入れてしまうため各ドキュメントに所有者情報が必要になります。そうすると各ドキュメントに入れるJSONは次のような感じになります。
{
    owner: "abcdefg"
    name: "山田太郎",
    address:"東京都港区赤坂xxxx"
}
各ドキュメントにデータの所有者のIDを入れて管理します。 データーを取り出す場合は、自分の所有しているデータのみを取り出します。
firebase.firestore().collection("address_book").
    where("owner","==',"abcdefg").
    get().then((querySnapshot:firebase.firestore.QuerySnapshot) => {
        .....
        // データを取り出した後の処理
    });
のようにすれば、本人の管理しているデータのみを取り出すことができます。 しかし、セキュリティルールを設定しないと全てのデータにアクセスできてしまうことになります。 この場合、セキュリティルールでは、リクエストしているドキュメントのデータをセキュリティルール内で取り出して、"owner"の値が自分自身のIDと同じかどうかをチェックする必要があります。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function get_document(dataId) {
        return get(/database/$(database)/documents/addrss_book/$(groupId))
    }
    match /address_book/{dataId} {
        allow read: get_document(dataId).data.owner == request.auth.uid;
    }
  }
}
のように書く必要があります。こうすると、自分のUIDと異なるデーターにアクセスしようとするとセキュリティルールでエラーになります。

「address_book」の下に書く利用者ごとの別のサブコレクションを作って利用者ごとにデータを分ける

これは、トップレベルに「address_book」というコレクションを持つのは同じですが、その下に所有者ごとの別のサブコレクションを作成して、所有者ごとにコレクションを分けてしまうという考え方です。
この場合、各ドキュメントのデータに所有者情報はいらないので、以下のようにJSONをシンプルにできます。
{
    name: "山田太郎",
    address:"東京都港区赤坂xxxx"
}
アクセスの際は、その人のサブコレクションのみアクセスすれば良いことになります。
const user:firebase.User|null = firebase.auth().currentUser;
if (user) {
    firebase.firestore().collection("address_book").
        doc(user.uid).
        collection("address_book").
        get().
        then((querySnapshot:firebase.firestore.QuerySnapshot) => {
            .....
        // データを取り出した後の処理
        })
}
Firebaseのコレクションにアクセスするための改装が一段階増えます。ドキュメントのIDいUIDを利用して、サブコレクションは同じコレクション名を使っています。
この場合も、セキュリティルールは必要ですが前の例よりはシンプルになります。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /address_book/{userId}/address_book{dataId} {
        allow read: userId == request.auth.uid;
    }
  }
}
これは、各サブコレクション毎にセキュリティルールを設定できるため、セキュリティルール内でコレクションのデータをチェックする必要がないからです。
Firebaseの課金の為のアクセス回数には、セキュリティルール内でのアクセスもカウントされます。 この方法を使うと、セキュリティルール内ではデータベースのデータにアクセスする必要がないために、少ないアクセス回数でデータにアクセス可能になります。

まとめ

必要な機能を実現するためのデータベースの構造は一つとは限りません。 データベース自体をシンプルにすることも大切ですが、Firebaseのデータベースを利用する場合は、データベースのアクセス回数は利用料金に影響しますし、セキュリティルールの設定の仕方もデータベースの構造によってかなり変わってきます。
最初にデータベースを設計する際に、セキュリティルールを意識するだけでも、実際の運用では大きな差が出る場合も沢山あります。特にFirebaseのデータベースは広く利用されているSQLなどのリレーショナルデータベースとは異なるタイプのデータベースです。
Firebaseにあった実装、データ構造を考える事が重要です。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す