Firestore基本知識とDB設計技法

# はじめに
Firebaseにおいて重要なFirestoreの機能と特徴、設計技法を大まかに解説します。

####この記事の対象とする人
> - Firebaseを導入しようか迷っている人
- Firestoreの特徴を大まかに知りたい人
- Firestoreの設計方法を知りたい人


```:言いたいこと
- Firestoreは従来のDBとは全く異なる
- Firestoreの設計に正解はないが、指針はある

```

#Firestoreを勉強するにあたっての現状　
* Firestoreに関する情報は少なく、変化が激しいので、正確な情報はFirebase[公式ドキュメント](https://firebase.google.com/docs)や[動画](https://www.youtube.com/c/firebase)から入手する必要があります。
* FirestoreはNoSQLである上に、セキュリティルールによってユーザーが直接データーベースにアクセスできる独自の方法をとっているため、**1から勉強が必要になります**。
* 「Firebaseは学習コストが低い」と言われていますが、正規化のような**一般化された設計技法がない**ため、適切な設計を行えるようになるには、時間と経験を要します。

いきなり厳しいことを言いますが、Firestoreの勉強を始めてこの辺りを強く感じました。

#基礎知識

##そもそもFirestoreとは
Googleさんが次の記事で解説しています。
[Firestore について知っておくべきこと: クイック リファレンス](https://cloud.google.com/blog/ja/topics/developers-practitioners/all-you-need-know-about-firestore-cheatsheet)

この記事によると
> Firestore はサーバーレスのフルマネージド NoSQL ドキュメント データベース

と紹介されており、Firebaseにおけるデータベースの機能であることが分かります。

Firestoreの特徴は、以下の3つを知ることで理解することができます。

1. サーバーレスである
2. NoSQLである
3. ドキュメントモデル データベース

###1. サーバーレスである
**サーバーのことを考慮せずにサービスを構築することができます**
サーバーの立ち上げや、スケールアップを自動的に行なってくれる[^1]ため、サーバーの知識が全くなくても、サービスを展開することができます。

これが理由でFirebaseを取り入れようと考えている人も多いのではないでしょうか

[^1]: データを分散して格納しているため、スケールアップした際に垂直スケーリング（マシンパワーを上げる）のではなく、水平方向スケーリング(マシンを増やす)ことが可能で自動で行われる。

###2. NoSQLである
[Firebase公式が出している動画](https://youtu.be/v_hR4K4auoQ)では、RDBのようなデータ構造とは以下の点が異なると紹介されています。


|項目名|設計の決まり|データの取得|データの重複|
|:-:|:-:|:-:|:-:|
|Firestore|一般化された答えはない|テーブルごとにクエリを書いて取得|推奨|
|RDB|正規化のような正解がある|結合によって一つのクエリで取得できる|非推奨|


###3. ドキュメントモデル データベース
これは、データの格納の仕方が以下のようになっていることを意味しています。

![データ構造](https://firebase.google.com/docs/firestore/images/structure-data.png?hl=ja)
 
```tex:
--collection : ドキュメントのみを格納できるフォルダ
    |--document : １MB以下のデータをフィールドに格納できる
        |--collection　: ドキュメント内のコレクションをサブコレクションとも呼ばれる
　　　　　　　　　　　　　　　　　　　　　　　　|          (サブコレクションのデータを取得すると、上位にあるデータを取得するため注意が必要)
            |--document
　　　　　　　　　　　　　　　　　　　　　　　　　  ...
```

コレクション→ドキュメント→コレクション...と続いていることがわかるかと思います。

このような、階層的な構造をとっていることがドキュメントモデルデータベースの特徴になります。
（collectionをいくつかまとめることのできるcollectiongroupもあります。）

それでは、どのような利点があるのでしょうか

##Firestoreの利点
####サーバーのことを考慮せずにサービスを構築することができる
すでに上で説明しましたが、サービス構築の労力が減ります。
####リアルタイム
リアルタイムリスナーによって、最新の情報を取得することができます。
####魅力的な機能がある
オフライン対応やバッチ処理を簡単に行うことができる上、FirebaseAuthによる認証機能、モニタリングやリソースの管理、簡単な機械学習など、さまざまな機能が公式から提供されています。


続いて、設計技法のうちの一部を掻い摘んで解説をします。
Firestoreを用いたサービス構築おいてDB設計は、正解がなく経験を要する作業になります。

#DB設計技法
###設計の一例を紹介
Firestoreでは設計に正解はなく、次のような指針に沿って構造を変化させる必要があります

　1. [課金額が抑えられた設計であるか](#課金額が抑えられた設計であるか)
　2. [簡単なクエリであるか](#簡単なクエリであるか)
　3. [セキュリティルールによって機密レベルを分けられるか](#セキュリティルールによって機密レベルを分けられるか)

###1. 課金額が抑えられた設計であるか
Firestoreの課金方法は少し特殊で、[公式ドキュメント](https://firebase.google.com/docs/firestore/pricing?hl=ja)では課金方法は次の通りであるとされています。
> 読み取り、書き込み、削除を行うドキュメントの数。

つまり、書き込み、読み込み、削除では**操作の数**、読み込みでは、**取得した数**によって課金額が決まります。
それゆえ、書き込みや読み込みの数を減らすデータベース設計が必要になります。

これらを踏まえると、以下のような設計を考えることができます。
> - データの重複（非正規化）を行う
> - 上位のコレクションにデータを配置する。
> - パスを置く

#####データの重複（非正規化）を行う
前もって取得するデータのまとまりがわかっている場合は、そのデータを同じドキュメントやコレクションにまとめることで、データの取得回数を減らすことができます。データをまとめる上で、同じデータが**重複**しても良いとするわけです。

#####上位のコレクションにデータを配置する。
また、サブコレクションにデータを格納すると、そのデータを取得するたびに、無駄な上位のデータのアクセスが行われてしまうため[^2]、アクセスが多いとあらかじめわかっているデータがある場合には、そのデータを上位のコレクションに配置するといった設計を行う場合もあります。

[^2]: コレクショングループに入れることで、サブコレクションによる無駄なデータ取得を避ける方法もあります。

##### パスを置く
データを重複させると、書き込みの際に多くの場所を更新しなければならず、面倒なバッチ処理が必要になるため、データのリクエストが極端に減る場合にのみ重複が行われ、重複させたいデータのパスを置くといった方法がとられることが一般的です。

###2. 簡単なクエリであるか
[データの取得](#NoSQLの特徴)で述べましたが、一つのクエリによって取得できるデータは、一つのドキュメント、コレクションの中のデータになります。複数のドキュメントやコレクションにまたがってデータを取得することができないため、複雑なクエリはあらかじめわかっているデータのまとまりごとに分けるという設計によって簡単なクエリにする必要があります。

###3. セキュリティルールによって機密レベルを分けられるか
####そもそも、セキュリティルールとは
これは、Firestoreが従来のdbとは異なる方法でデータを取得していることから分かります。

|項目名|DBへのアクセス|
|:-:|:-:|
|Firestore|セキュリティルールによって、ユーザーが直接データベースにアクセスする|
|従来のDB|アプリケーション層を介して、ユーザーの手の届かない位置にあるデーターベースから値を取得する|

セキュリティルールは、従来アプリケーション層で行われていたビジネスロジックの実行の代わりとして行われるものになっています。

セキュリティルールによって
 
* 権限の確認
* バリデーション
* スキーマ検証
 
が行われ、ユーザーが行うことのできる操作を制限したり、値が正しいかをチェックしてデータベースのセキュリティの不具合を防いでいるというわけです。

そしてこのセキュリティルールは、次のように記述されます。

```javascript:セキュリティルールの例
match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
}
```
1行目の記述により、**ドキュメントやコレクションごと**にセキュリティルールを定めることができます。

それゆえ、
**同じドキュメントの中に公開して良いデータと非公開のデータが混在している場合、公開や非公開の設定を個別に行うことができません。**


よって、

**データ機密レベルが異なる場合は、別のドキュメントやコレクションに格納する**

ことで、それぞれの機密レベルを担保する必要があります。

######機密レベルによって、データを分ける
といった設計をするわけです。

#まとめ
###基礎知識
Firestoreの特徴は、
1. サーバーレスである
2. NoSQLである
3. ドキュメントモデル データベース

利点は、
1. サーバーのことを考慮せずにサービスを構築することができる
2. リアルタイム
3. 魅力的な機能が公式から提供されている

###DB設計

　1. 課金額が抑えられた設計であるか
　2. 簡単なクエリであるか
　3. セキュリティルールによって機密レベルを分けられるか

という指針に沿ってDB設計を行うと、
> - データの重複（非正規化）を行う
> - 上位のコレクションにデータを配置する。
> - パスを置く
> - 取得するデータのまとまりに分ける
> - 機密レベルによって、階層を分ける

といった設計技法を考えることができる。