Angularにおける状態管理の方法について検討したので、雑にまとめます。
状態管理とは
まず状態管理とは、画面表示やデータ同期のために必要な情報を管理すること かなと思います。
具体的な例をあげると、下記のようなものがあるかと思います。
- Todoアプリケーションで、Todoリストのデータを保持し、追加・削除・編集が行われた時に、Todoリストのデータを更新する
- データの読み込み中にはローディングを表示したいため、読み込み中かどうかの状態を管理する
状態管理方法
Angularで状態管理を行おうとした場合、アプリケーションの規模に応じて適した状態管理方法が変わってくる。
- 小規模:Serviceを使って、独自で状態管理を実装する
- 大規模:Flux、Reduxアーキテクチャを利用し、状態管理を実装する
Serviceを使って、独自で状態管理を実装する
AngularのServiceを使い、自分で状態管理を実装する方法。
Todoアプリケーションであれば、Todoリストの保持やTodoの更新を行う処理をTodoServiceに実装し、コンポーネントでTodoリストを扱いたい場合にはこのTodoServiceを利用する(一例)。
RxJSを利用することで、状態の変更をトリガーに処理をかけたりするので、より宣言的な実装になる。
ただ、この独自で状態管理を実装した場合、下記のような課題感があると思う。
- 実装方針をチーム内で決めないと、オレオレな実装になり保守性が下がる。
- アプリケーションの規模が大きくなると、状態管理が煩雑になる。どこになんのデータがあるのか、データ更新時にはどこのデータをどういうふうに更新すればいいのかなど、複雑性がましていく。
- 関心ごとにServiceを切り出していったときに、コンポーネントが依存するServiceが多くなったりする。再利用性の低下やテスタビリティの低下など
なので、アプリケーションの規模が大きくなってきた場合は、FluxやReduxアーキテクチャを利用する。
Flux、Reduxアーキテクチャを利用し、状態管理を実装する
Fluxは、単一方向のデータフローを実現するアーキテクチャ。
複雑になりがちなデータフローを単一方向にすることで、開発者が状態の流れを把握しやすくなる。
Reduxは、Fluxの単一方向のデータフローと、Elmの純粋関数による副作用の排除や、イミュータブルな状態表現の制約を踏襲し、より状態変化を予測可能にしようとしている。
状態変化を予測可能にするためのReduxの3原則↓。
- 信頼できる一意となる状態を唯一とする(Single source of truth)
- それぞれのコンポーネントが同じ状態を参照することで整合性の取れた画面表示になる
- 状態はイミュータブルで表現する(State is read-only)
- Storeで管理しているStateオブジェクトをイミュータブルにすることで、状態が変更されることを防ぐ。
- 状態更新する時には、既存のStateオブジェクトから新しいStateオブジェクトを生成し、それをStoreに登録することで状態の更新を行う。
- そうすることで、状態を変更する箇所が明確になり、より状態の変化を把握しやすくなる。
- 状態の変更は純粋関数で記述する(Changes are made with pure functions)
- 状態更新を行うReducerが純粋関数であることで、既存のStateオブジェクトとActionから新しいStateオブジェクトを生成すること以外に副作用が生まれないことを保証する。
ngrxや、ngxsといったライブラリを利用することで、手軽にAngularでReduxを実装することができる。
まとめ
- 小さいアプリケーションであれば、Serviceで状態管理を実装するでこと足りそう
- または、シンプルな状態管理ライブラリを使う(akitaとか)
- 大規模なアプリケーションや将来的にスケールしそうなアプリケーションであれば、最初からReduxアーキテクチャを利用して状態管理していると保守性が上がりそう。
- アプリケーションやその時の状況に応じて適切な状態管理方法を選択できるようにしていきたい。