22
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Qiitaを題材に、FirestoreのDB設計について考えてみる。

Posted at

はじめに

この度、個人開発にて、Qiitaライクな投稿型ブログサービスをFirebaseを利用して制作しました。
本経験を基に、改めてFirestoreのDB設計について考えてみたいと思います。
※なお、完全にQiitaを再現するものではなく、一部機能やデータは簡略化のため削除しています。

作成したサービス
Share Hondana

本記事では、「プロフィール」と「投稿記事」の2つのデータ設置について取り上げます。
「LGTM(いいね)」、「フォロー・フォロワー」、「コメント」などについては、別記事で記述する予定です。

以下、断定調にて失礼します!

DB設計における選択肢

FirestoreのDBは、常時複数のパターンで設計することができる。
設計を考えるにあたっては、下記のような観点において選択肢が分かれる。
・コレクションをトップレベルに置くか、サブコレクションとするか。
・データを冗長化させるか、単一にまとめるか。

ここからQiitaを題材に、実際にDB設計について検討してみる。

プロフィール情報

まずはプロフィール情報の設置について考えてみる。
プロフィール情報は、トップレベルに設置したusersコレクションのドキュメントに格納するのが良さそうである。

usersコレクションのドキュメントに格納

users(コレクション): {
  ユーザー名:近藤
  ユーザーアイコン:imgのデータ
 自己紹介文:フロントエンドエンジニアを目指しています。内定が欲しいです...。
  ※その他、「公開用プロフィール」のデータはここに格納する。
}

スクリーンショット 2021-09-24 233749.png

「公開プロフィール」以外のユーザー自身のみ閲覧・操作できる項目は、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して頂くと、次回以降の記事の質がアップします。(小声)

22
23
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?