はじめに
この度、個人開発にて、Qiitaライクな投稿型ブログサービスをFirebaseを利用して制作しました。
本経験を基に、改めてFirestoreのDB設計について考えてみたいと思います。
※なお、完全にQiitaを再現するものではなく、一部機能やデータは簡略化のため削除しています。
作成したサービス
Share Hondana
本記事では、「プロフィール」と「投稿記事」の2つのデータ設置について取り上げます。
「LGTM(いいね)」、「フォロー・フォロワー」、「コメント」などについては、別記事で記述する予定です。
以下、断定調にて失礼します!
DB設計における選択肢
FirestoreのDBは、常時複数のパターンで設計することができる。
設計を考えるにあたっては、下記のような観点において選択肢が分かれる。
・コレクションをトップレベルに置くか、サブコレクションとするか。
・データを冗長化させるか、単一にまとめるか。
ここからQiitaを題材に、実際にDB設計について検討してみる。
プロフィール情報
まずはプロフィール情報の設置について考えてみる。
プロフィール情報は、トップレベルに設置したusersコレクションのドキュメントに格納するのが良さそうである。
usersコレクションのドキュメントに格納
users(コレクション): {
ユーザー名:近藤
ユーザーアイコン:imgのデータ
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
※その他、「公開用プロフィール」のデータはここに格納する。
}
「公開プロフィール」以外のユーザー自身のみ閲覧・操作できる項目は、usersコレクション配下にサブコレクションとして設置したい。
サブコレクションに設置することで、ユーザー自身しか閲覧・操作できないようにセキュリティールールを設定することができるからだ。
このように、全ユーザーが閲覧できるのか、否かといったユーザーの権限によって、コレクションを分ける場合があるのはポイントである。
認証系はAuthenticationの役割だが、例えばQiitaの「ストックした記事」はユーザー自身しか閲覧・操作できないデータであるため(多分)、サブコレクションに設置するのが良さそうだ。
users(コレクション): {
ユーザー名:近藤
ユーザーアイコン:imgのデータ
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
stockPosts(サブコレクション):{
記事A:記事Aの情報
記事B:記事Bの情報
}
}
要点
・公開情報はコレクションをトップレベルに置く。
・非公開情報(ユーザーが限定される情報)は、サブコレクションに設置することで、セキュリティールールを付与する。
投稿記事の情報
次に、投稿記事のデータ設置について考える。
2パターン思いついた。
①トップレベルにpostsコレクションを設置
users(コレクション):{
省略
}
posts(コレクション):{
記事A: {
執筆者:近藤
タイトル:Qiitaを題材に、FirestoreのDB設計について考えてみる。
作成日:2021-9-24
内容:この度、個人開発にて、Qiitaライクな投稿型~~
}
記事B:{
執筆者:近藤
タイトル:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
作成日:2021-9-24
内容:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
}
記事C:{
執筆者:佐藤
タイトル:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
作成日:2021-9-24
内容:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
}
}
②usersコレクション配下のpostsサブコレクションとして設置
users(コレクション): {
ユーザー名:近藤
ユーザーアイコン:imgのデータ
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
posts(サブコレクション): {
記事A: {
タイトル:Qiitaを題材に、FirestoreのDB設計について考えてみる。
作成日:2021-9-24
内容:この度、個人開発にて、Qiitaライクな投稿型~~
}
記事B:{
タイトル:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
作成日:2021-9-24
内容:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
}
}
}
メリット・デメリット
①トップレベルにpostsコレクションを設置
メリット・・・記事一覧の取得が容易。
デメリット・・・執筆者名など、執筆者のプロフィール情報を格納する必要がある。記事を表示する際は、ユーザー名、ユーザーアイコンなどの情報も必要である。
②usersコレクション配下のpostsサブコレクションとして設置
メリット・・・誰の記事かが明確である。執筆者の情報が不要(に一見すると思える)。
デメリット・・・全記事を一覧で表示するトップ画面の処理は複雑になる。ここについては、詳しく検討したい。
postsをusers配下のサブコレクションに設置した場合、全ユーザーの記事を一覧で取得する場合はどう処理するだろうか。
全ユーザーのIDを全て取得した上で、各ユーザーの投稿記事を取得するのはあまりに非効率である。
ここについては、CollectionGroupを用いることで、直接users配下のpostsサブコレクションをまとめて取得できるので問題なさそうだ。
待ち焦がれたCollectionGroupがCloud Firestoreへやってきた。
しかし、CollectionGroupを使用するとユーザーのプロフィール情報が取得する術が思いつかない。
Qiitaのトップ画面を思い出して欲しい。記事のタイトルやタグに加えて、投稿者のアイコンや名前がセットで表示されていたはずだ。
結局のところ、postsをusers配下のサブコレクションに設置した場合においても、ユーザーのプロフィール情報を格納する必要がある。
結局こうなる、、、
users(コレクション):{
省略
posts(コレクション):{
記事A: {
執筆者:近藤
ユーザーアイコン:アイコンimg
タイトル:Qiitaを題材に、FirestoreのDB設計について考えてみる。
作成日:2021-9-24
内容:この度、個人開発にて、Qiitaライクな投稿型~~
}
記事B:{
執筆者:近藤
ユーザーアイコン:アイコンimg
タイトル:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
作成日:2021-9-24
内容:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
}
}
}
これではpostsをサブコレクションに設ける意味がない。
よって、postsはコレクションとするのが正着だと判断する。
postsコレクションの内のプロフィール情報
上記までで、postsコレクションにはプロフィール情報を格納すると考えてきた。しかし、ここには1点懸念がある。
ユーザーがプロフィールの情報を変更した場合、これまでに当該ユーザーが投稿した記事に含まれるプロフィール情報を全て更新しなければならない。
これが、データを冗長化させた場合の欠点である。
では下記のように、記事投稿者のIDのみ格納してはどうだろうか。
posts(コレクション):{
記事A: {
執筆者ID:asdfgjgslkjdfl
タイトル:Qiitaを題材に、FirestoreのDB設計について考えてみる。
作成日:2021-9-24
内容:この度、個人開発にて、Qiitaライクな投稿型~~
}
記事B:{
執筆者ID:asdfgjgslkjdfl
タイトル:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
作成日:2021-9-24
内容:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
}
記事C:{
執筆者ID:bnmsdofijgoai
タイトル:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
作成日:2021-9-24
内容:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
}
}
例えば、ユーザーがA記事を表示したとする。その際、投稿者の名前やアイコンのデータも必要である。記事Aが格納されている執筆者IDを利用して、一致するusersコレクションの情報を取得することができる。
記事を表示するにあたっては、記事の取得→投稿ユーザーの情報取得という2回のクエリを要することとなる。
①postsコレクションに必要なプロフィール情報を全て格納する。
ユーザーがプロフィールを変更した際は、大量の記事のデータを更新する必要があるかもしれない。
つまり、データの変更に弱い。
②postsコレクションに投稿者のIDを格納する。
記事を表示するにあたっては、2回のクエリが発生する。
つまり、データの取得に弱い。
考えるべきなのは、どちらのほうがDBへのアクセスが少なく済むかという点である。
これは断然①のほうが、少なくて済むのではないだろうか。
1ユーザーの投稿数は、限られている。仮にプロフィールに更新があった場合でも、せいぜい数十記事である。
それと比較して、記事を表示する機会は非常に多い。私のつたない記事でさえ、1,000viewsは楽に超えてしまう。
以上を考えると、①が良さそうだ。
データの変更には弱いが、読み取りに強いDBを設計できるという点は、Firesoteの強みである。
まとめ
ここまでをまとめると下記のようなDBとなった。
users(コレクション):{
ユーザー名:近藤
ユーザーアイコン:imgのデータ
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
サブコレクション:{
ユーザー自身しか閲覧・操作できないものはサブコレクションに置く。
}
}
posts(コレクション):{
記事A: {
執筆者:近藤
ユーザーアイコン:img
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
タイトル:Qiitaを題材に、FirestoreのDB設計について考えてみる。
作成日:2021-9-24
内容:この度、個人開発にて、Qiitaライクな投稿型~~
}
記事B:{
執筆者:近藤
ユーザーアイコン:img
自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
タイトル:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
作成日:2021-9-24
内容:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
}
記事C:{
執筆者:佐藤
ユーザーアイコン:img
自己紹介文:ベンチプレスのMax150kg。UNDER ARMOURのマネキンのような胸筋を目指す。
タイトル:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
作成日:2021-9-24
内容:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
}
}
最後に
上記に示したのは、あくまで一例です。他の設計も考えられるかと思います。
また、私自身Firestireを正確に理解できているとは言い難く、間違いもあるかもしれない点ご了承下さい。
「LGTM(いいね)」、「フォロー・フォロワー」、「コメント」などについては、別記事で記述する予定です。
本記事にLGTMして頂くと、次回以降の記事の質がアップします。(小声)