Open9

実践 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 でショッピングサイトを実装してみる

復習のためにまた今度読みます。