マインドマップを用いてRedux-sagaとstoreの構成を整理した話



これは [React Advent Calendar 2019](https://qiita.com/advent-calendar/2019/react) の22日目の記事です。

## はじめに

先日、[「TwitterやSlackのRedux Storeを覗く」](https://engineer.recruit-lifestyle.co.jp/techblog/2019-12-20-dissecting-redux-store/)という記事が話題になっていました。そこでも述べられているように、Reduxを用いたアプリケーションではStoreの設計が重要になってきます。筆者も、最近友人とReact(Redux+ReduxSaga)+FirebaseのWebアプリを開発する中で Storeの構成についてや、Redux-Sagaを呼び出すタイミングの整理をしたシーンがありました。その際、[PlantUMLのマインドマップ](https://plantuml.com/ja/mindmap-diagram)を用いたところ、スッキリと情報を整理することができました。この記事では、どういった経緯でマインドマップを利用することになったのか、またどのように活用したのかということを書いていきます。


## 特定のSagaを呼び出す適切なタイミングを考える必要があった

ReduxとRedux-Sagaを用いて開発を進める中で課題に感じていたことが**「様々にあるSagaはどのタイミングで飛ばすべきなのか、どのタイミングで飛ばさなければいけないのか」**ということです。またそれに関連して「画面をレンダリングする時に必要なstateが存在しないという状況を無くしたい」ということも感じていました。

### 何がいけなかったのか

上述した課題に直面した原因は**「stateの依存関係およびそのstateを取得するSagaを飛ばすタイミングを考慮していなかったこと」**です。この状況の例えとして、Twitterのようなフォローユーザー一覧画面やタイムライン画面を持つアプリを想定して考えてみます。

![スクリーンショット 2019-12-22 17.46.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255852/690c3d4a-6375-5346-6d04-50b385e10617.png)

上の図の中では、ホーム画面、フォローユーザー一覧画面、タイムライン画面の三つの画面があります。そして、それぞれの画面がそれぞれの画面の描写に必要な```state```を持っています。ここでは、フォローユーザー一覧画面をレンダリングするタイミングでSagaを飛ばして```stateC（フォローユーザー情報）```をFirestoreに問い合わせています。一方で、タイムライン画面は```stateD(タイムライン情報)```を持っているのですが、**これは```stateC（フォローユーザー情報）```に依存しているstateになります。フォローしているユーザーの情報が無ければタイムラインを表示できないからです。**ここにstateの依存関係があるのですが、これを考慮していなかったためフォローユーザー一覧画面を経由してからでないとタイムラインが正常にレンダリングできなかったり、タイムライン画面をレンダリングするタイミングでもう一度フォローユーザー情報を取得するSagaを飛ばさなければいけないという冗長な作りになっていました。

こういった問題が浮き彫りになったため、情報を整理するため[PlantUMLのマインドマップ](https://plantuml.com/ja/mindmap-diagram)を用いて考えてみます。


## マインドマップを用いて整理する

前述した状況をマインドマップを用いて表現したのが以下の図です。

![スクリーンショット 2019-12-22 18.15.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255852/078526c8-4c5e-c423-1759-34ce779a7e23.png)





先ほどの流れで、まずは画面ベースでstateを考えてみます。このマインドマップでは各画面が持つ```state```を末端に表しています。ここで、Sagaについて考える上での観点が二つあります。一つ目は、**```state A```がホーム画面とフォローユーザー一覧画面で重複していること**、二つ目は**タイムライン画面において```state D(タイムライン情報)```が```state C(フォローユーザ情報)```に依存していることです**。

まずは一つ目について考えます。```state A```に関して、それぞれの画面をレンダリングする時に別々のSagaによって取得することは冗長です。なので、```state A```を取得するSagaは1回だけにして、認証後の初期外部リクエスト(initial saga)によって該当データを取得することにします。

次に二つ目についてです。```state D(タイムライン情報)```は```state C(フォローユーザ情報)```に依存しています。つまり、```state C```が無ければ```state D```も表示させることができません。そこで、プロフィール画面もしくはタイムライン画面個別に紐づいたSagaではなく、一つのSagaとして```state C```を取得することにします。こちらも認証後の初期外部リクエスト(initial saga)で飛ばすのが良さそうです。


![スクリーンショット 2019-12-22 18.41.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255852/6e5a92b9-e5c8-74c2-aa3d-1b1d6a45f6a0.png)



ここまでPlantUMLのマインドマップを用いてstateとSagaについて考えてみました。ここで、stateとSagaについてまとめてみます。下図のように、二つの画面で重複していた```state A```と、依存関係にあった```state C```はまとめて認証後の初期外部リクエスト(initial saga)によってデータを取得するようにしました。

![スクリーンショット 2019-12-22 23.13.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255852/fa282cb0-7447-191a-d48f-c0f3624e6e12.png)



また、Storeに関しては下図のような構成になっています。最初は画面ベースでstateを考えていましたが、```state A```が複数の画面で重複しているため冗長な構成を避けたいこと、開発規模が小さいことから最終的には画面ベースではなくドメインベースの構成になっています。これらに加えて、開発中のアプリのビューは完全に決定してるわけではないので、後々のビューの変更にも強いドメインベースを採用しています。画面ごとに分担作業するなどの場合は冗長でも画面ベースで考えるというのはありかもしれません。ちなみに、Redux公式ドキュメントはドメインベースでのStore設計を推奨しています。また、Store設計に関しては[「TwitterやSlackのRedux Storeを覗く」](https://engineer.recruit-lifestyle.co.jp/techblog/2019-12-20-dissecting-redux-store/)の記事が勉強になりました。

![スクリーンショット 2019-12-22 19.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255852/49d9bdca-3ba1-f82d-9125-d0f1154bf949.png)


## おわりに

今回、マインドマップを用いてSagaの呼び出し順序やStoreの構成を考えてみました。ReduxやRedux-Sagaを使用し複雑なビジネスロジックになる場合、整理のためにマインドマップを用いてみてはどうでしょうか。また、Storeの設計を考える上で[「TwitterやSlackのRedux Storeを覗く」](https://engineer.recruit-lifestyle.co.jp/techblog/2019-12-20-dissecting-redux-store/)の記事はとても参考になると思いました。