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

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

作成したサービス 
[Share Hondana](https://sharehondana.web.app/)

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

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

#DB設計における選択肢

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

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

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

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

```
users(コレクション): {
  ユーザー名：近藤
  ユーザーアイコン：imgのデータ
　自己紹介文：フロントエンドエンジニアを目指しています。内定が欲しいです...。
  ※その他、「公開用プロフィール」のデータはここに格納する。
}
```
![スクリーンショット 2021-09-24 233749.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1766904/6a6f3025-0a11-0637-e551-29beee2325e0.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へやってきた。](https://qiita.com/1amageek/items/343f262ba3b50e657078)

しかし、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して頂くと、次回以降の記事の質がアップします。(小声)