GCP Firestore: データの関係性ごとに適当な設計を考えた
目次
- なぜこの記事を書いたのか
- Firestore とは
- 設計について
- まとめ

なぜのこの記事を書いたのか
直近一年間に GCP の Datastore, Firestore, AWS の DynamoDB など色々なKVSを触る機会に恵まれました。 もともとRDBを使っていたため最初はKVSの設計にかなり戸惑ってしまいました。 これから使う人の参考になればと思い、データの関係性ごとにどう設計すれば良いのか今の考えをまとめました。
Firestore とは
Firestore は、柔軟でスケーラブルなリアルタイム データベースです。プロトタイプを迅速に作成できるだけでなく、どのような規模の開発にも対応できるスケーラビリティと柔軟性を兼ね備えています。 Firestore はリアルタイム データベースであるため、クライアントは Firestore のデータをリッスンして、変更が生じたときにはリアルタイムで通知を受け取ることができます。この機能により、ネットワーク レイテンシやインターネット接続に関係なく動作する応答性の高いアプリを構築できます。
設計について
1:1 のデータ
基本的に1対1のデータは1つのドキュメント(RDBでいうレコード)に保存する RDBと違ってSchema定義も必要なく、1つのドキュメントで1 MiBまで保存できるのでほとんど気にすることはないと思います。
ただ、RDB違って Field を指定して取得することができないため、 住所やパスワードといった機密情報を別のユーザーが取得できるとセキュリティの問題があるため、 別の Collection に保存し、セキュリティグループで本人のみデータを取得できるように制約をかけることを検討した方が良いです。
1:N のデータ
Nに上限がある場合
1つのドキュメントに配列として保存した扱うとドキュメントの取得する数を減らせてコスト削減できるため1つのドキュメントにまとめることをおすすめします。
ただ、Nのデータ単体で表示したりソートをする必要がある場合は扱いにくいため別のCollectionにすることを検討すると良いと思います。
Nに上限がない場合
1 MiBの制限に引っかかる可能性があるためSubCollectionや別のCollectionに分けて保存する必要があります。 別Collectionにする場合、毎回1のデータを取得しなくても済むように、データのコピーをNのデータに持つことを検討すると良いです。
以下のようなイメージです
purchase
{
"price": "number",
"count": "number",
"purchased_at": "timestamp",
"user": {
"id": "string"
"name": "string"
"address": "string"
"phone_number": "string"
}
}
以下ようにするとN+1クエリが発行されたり、クライアントでデータをくっつける必要が出てきて非効率になるため、 仕様上問題がなければ非正規化した方が良いと思います。
purchase
{
"price": "number",
"count": "number",
"purchased_at": "timestamp",
"user_id": "string"
}
N:N のデータ
N対Nの場合は、1ドキュメントでは表現できないため基本的に複数の Collection を使うことになります。
基本的には新しく関係性を表現するCollectionを作成すると良いと思います。
shops_tags
{
"shop_id": "string",
"shop_name": "string",
"tag_id": "string",
"tag_name": "string"
}
ただ、シンプルに記述したくて利用頻度が多くない場合は、どちらかにもう片方の配列を保持することでも実現しても良いと思います。
shops
{
"name": "string",
"tags": [
{
"id": "string",
"name": "string",
},
{
"id": "string",
"name": "string",
}
]
}
まとめ
- Firestoreの設計をするときに最初に検討した方が良いことをまとめました。
- RDBとは考え方が違うので最初は非常に戸惑うと思います。
- データの関係性ごとにどう設計するかまとめたので最初は参考にして設計すると良いと思います。
ネット上の記事や、ドキュメントを色々みましたが、以下の動画がかなり参考になったので一度みてみると良いと思います。 www.youtube.com