実践 Firestore を読む
読書感想文です
実践 Firestore Amazonリンク
を読んで、Firestore の理解をもっと深めようという趣旨のスクラップです。
感想
めちゃくちゃいい本でした。
何を基準にデータ設計すればいいかの見通しがかなりつくと思います。
おためしあれ。
はじめに
NoSQL系のデータベースには一般化されたベストプラクティスが存在しない。
- 高いスケーラビリティ
- 高いパフォーマンス
- 十分な信頼性
これらを備えたアプリケーション制作を目指す。
第 1 章 Firestore の正体
クライアントから直接アクセスを許可する。こんなこと今までなかった。
従来はビジネスロジックをサーバーサイドにまかせていた。
- セキュリティの問題
- パフォーマンスの問題
- 運用・保守の問題
- 信頼性の問題
- 複数プラットフォームへの対応
- 要件変化への対応
これらのバランスをとりながら解決する必要あり。
Firestore は ドキュメント指向 NoSQL データベースらしい。
Firestore から取得したデータをすぐそのまま利用できる、この利点を活かしたデータ設計にする。
アプリとデータベースの結合度をできるだけ小さくする。
スキーマ変更を恐れるな。もはやバックエンドでなんとかすることはできない。
無駄な読み取りオペレーションをしないための、リアルタイム・リスナー。
FIrestore はオフライン対応に優れているらしい。
オペレーション・キューが実装されている。
オフライン状態でもエラーになることがないから、クライアントが書き込んだタイミングと、データベースに反映されるタインミングがずれることが多い。その間、どのような表示をユーザーに与えるか、慎重に設計する必要あり。
読み取りに関しても、キャッシュを使える。
Cloud Functions Admin SDK で安全にユーザー同士に相互に影響を与えるような処理を実行できる。
ただ単に Firestore にアクセスしたいだけなら Functions は使うべきではない。
故障率はあがり、レイテンシーも大きくなる。
いいデータモデルの必要条件
- Funtions を使わずに読み取れる
- セキュリティールールの扱いが容易である
ロケーションは物理的に近い方が有利。
asia-northeast1 (東京) がいいよ。
第 2 章 データアクセスの基礎
小さいドキュメントを大量に扱うのに優れている。
大きいドキュメントを扱うのには優れていない。
map とか list に馬鹿でかいデータを突っ込んでいくのはあまりよろしくない。
クエリでの取り回しも難しい。
コレクショングループにより、同一名のコレクションを同時に扱うことができるぞい。
リファレスを使いこなすことが重要。
スナップショットに含まれるフラグ。
- fromCache
- キャッシュからデータを取得したよというお知らせ
- hasPendingWrite
- まだサーバーに書き込みされてないデータがあるよ
CRUD 操作
IDは衝突することがあるけど、衝突した場合はupadateとして扱われる。
それならそこで適切なエラーハンドリングを行うべきだよね。
AdminSDK にはセキュリティールールが関係ないので、衝突に気づけない。
トランザクション利用して同一IDがないかチェックした方がよい。
update は存在しないドキュメントに対しては、エラーになる。set はクリエイトになる。
set の場合、デフォルトでは、指定しなかったfieldは削除される。
merge を true にすれば、update相当の動作になる。
Firestore のクエリは強い生合成がある。
常に最新のものをとってきていると考えてよい。
複合クエリを使いたい場合は、index を作成する必要あり。
ページネーションを使って適切に必要なデータだけとってこれるようになったほうがいいよね。
レイテンシ補正
listenしている場合、ローカルで更新があった場合にすでに通知される。
hasPendingWrites が true になっている。
トランザクション
- 変更したいデータをまずは全部取得、その後更新。失敗したら全部ロールバックされる。
バッチ
- 全部書き込んだ後、commit で一気に変更がかかる
FieldValue.increment()
負の値を渡せば減少する。
FieldValue.arrayUnion() arrayRemove()
配列を追加したり、取り除いたり。ハッシュタグなどで使える。
冪等な処理 = 同じ操作を繰り返しても、同じ結果になる処理。
.increment とかは同じ結果にならないよね。
第 3 章 オフラインモード
開発者が意図しなくても、なんかオフラインで動いちゃう、ここをまず意識する。
- オフラインデータの永続化
- 不安定な状況に耐性のある読み書き
web では無効になってるので enablePersistence を呼ぶとよい。
また、複数タブで動機が取れるようにした方が良い。Flutter でのやり方は調査する必要あり。
オフライン状態で書き込みしようとすると非同期処理で待機する。
オンラインになったらちゃんと書き込んで終了する。
書き込み中のダイアログとか出す場合、オフライン状態だとずっと特にエラーも出ず表示され続けてしまう問題が発生する。
トランザクションの場合は、オフライン状態だとエラーが返ってくる。
読み取りの話
getOption で指定可能
- default
- server
- cache
がある。
キャッシュ優先読み取り
キャッシュがあればまずはキャッシュをとってこようよ、という考え方、自分以外の誰かに高い頻度で更新されるデータに対しては行うべきでない。
第 4 章 セキュリティルール
その特性を理解して、データ設計を考えよう。
- 認証と認可
- スキーマ検証
- バリデーション
1 ユースケース 1 ルールの原則を守れ。
結合度と凝集度が高いものを目指せ。
1 つのユースケースが修正されるなら、修正されるべきルールは 1 つのはずだ。
カスタムクレームで柔軟にuserに対して情報を付与し、それをもとにルールをかける。
端末のタイムスタンプではなく FieldValue.serverTimestamp() を使おう
ルールでエラーが出ても、何のエラーなのかはセキュリティの観点からあかされない。
だからこそしっかりテストコードを書いてチェックしようね。
アトミックオペレーションのルール作成はむずい。必要性にかられた時に、ゆっくり勉強する。
第 5 章 FIrestore データモデリング
- フィールドの機密レベルを統一する。
- 加工しなくてよいモデル (つまり、とってだしで使えるモデル)
- Widget に使われる情報をできるだけ一つのドキュメントに詰め込む
- ドキュメントは小さくシンプルに
- タイムスタンプが本当に必要か吟味する
- アトミックオペレーションに利用できるかもしれないことは心に留めておく
- 更新の少ないコレクション設計
- フラグを使うのではなく、コレクションそのものを分離できないか考える
1 : 1 はリファレンスを使おう。
ドキュメントIDではなくリファレンス。
非正規化してコストを下げる。重複を許す。
その場合でも ref は残しておく。
レビュー投稿に、ユーザー情報が内包されている場合、
正規化されている状態だと、必ず2回分の読み込みが発生してしまう。
けど、レビュー投稿内に、冗長にユーザー情報を持たせておくと、1回分の読み込みで大丈夫。
では、ユーザー情報が更新されたとき、最新に保つにはどうする?
ここで cloud functions を使う。
更新をトリガーとして、過去に登録されたデータ(今回はレビューの中に内包されるユーザー情報)をまとめて更新する。
ということは、頻繁に更新される可能性のあるデータを冗長に持たせると、それはそれでコストが高いってことがわかる。
基本は結合で実装してみよう。次にキャッシュ優先が使えないかを考える。最後に非正規化。
同一IDによるリレーション。
たとえば、大福をひとつ買い物かごにいれる。
大福ID と count をもった doc が作られる。
大福がもう一つ入った場合は、たんに count を更新する。
1 : n リレーション
商品ページに新着レビュー3件みたいなパターン。
コレクショングループを適切に使うために、命名には十分注意する。
コマンドクエリ責務分離
読み取りと書き込みでドキュメントを分割する。
書き込み専用ドキュメントに追加すると、それをトリガーに cloud functions がはしり、読み取り専用を更新する。
第 6 章 Firestore でユーザーを管理する
復習のためにまた今度読みます。
第 7 章 Firestore でショッピングサイトを実装してみる
復習のためにまた今度読みます。