Flutterのアーキ
※ twitterでつぶやくには量が多く、まともな技術記事にくらべたら中身スカスカなので注意されたい。手抜きメモレベルなので図もない。
最近仕事や個人で再びflutterを触る機会が増えてきたので再度flutterでどうアプリ構成すべきか考えることになった。
今は大まかにロジックを担当するUsecaseレイヤー、View側のロジックやUIのイベントをキャッチしてUsecaseを実行するIFとしてのPresenterレイヤー、画面構成するUIレイヤーで分けている。
最近は下に記す2つに頭を悩ませている。
- Presenterレイヤーの機能の分け方が適切であるか、細かすぎないか
- RiverpodのStateNotifierProviderの使い方が公式想定から外れていないか
1. Presenterレイヤーの機能の分け方が適切であるか、細かすぎないか
今はUIをあるエリアごと、コンポーネントごとに分割し、それぞれに1:1対応するようにRiverpodのStateNotifierProviderとそこで管理するViewModelを定義している。
このようにした理由は画面の中に動的な表示制御やデータソートやらフィルタリングのView側ロジックが多い場合、1つの画面に対してProviderとViewModelを1:1対応で定義するとそこがFatになりやすいと考えたからが1つ。
また、画面要件が変わりエリアを別画面に移動したり、他画面に同様のパーツを再利用したい時に1画面でProviderとViewModelをまとめているとそれを切り離すのがめんどうと考えたからが2つ。
ただこれは細かすぎるのではないかと思い始めてる。他のパーツのUI操作によって表現をかえるパーツがいた場合にわざわざ1つ上の画面階層のProviderにアクセスしないといかず1アクション冗長になってしまう。
さらにProviderやViewModelのボイラーテンプレート的なコードが多数生まれてしまう。(これが非常にめんどい)
さらにダメ押しでフロント開発で再利用可能なUIオブジェクトというものは実は少ないという経験則がある。
基本的なUIパーツはフレームワーク側から提供されており、それをそのまま使えば機能が実現する。そこに何かしらのカスタマイズをすることはあるが、大方デザイナーがデザインしたおしゃれなUIのために既存のボタンの見た目を設定して再利用可能にするくらいのものである。
見た目レベルで汎用化することは多いが、View側ロジックふくめて再利用できることは少ないので、ボイラーテンプレートや状態更新の冗長さを上回るほどのメリットが細かいパーツ分割がない気がしてきた。(もちろんUIはコンポーネント分割するのはいい。宣言的UIで1つのファイルでまとめて記述するとすぐに1000行達したりするので...あくまでPrenseter階層まで分割する必要はないという思想)
2. RiverpodのStateNotifierProviderの使い方が公式想定から外れていないか
1で少し記載したが、各UIパーツにStateNotifierProviderが割り当てられ、UI側からのイベントを対応するProviderが吸い上げ、さらに画面に対応するProviderまで吸い上げてからUsecaseを実行したり状態更新をしている。
これ画面に対応するProviderだけStateNotiferProviderにして他は普通のStateProviderで良くないか??
画面に対応するProviderで画面内のすべての状態を保持・状態更新を担当。各UIパーツはStateProviderで画面のProviderを参照して状態を取り出し、それをUIパーツ特有の表現制御ロジックをもったViewModelに突っ込めばいい。
状態はすべて画面階層にまとまるし、Fatになりやすい部分はすべて各UI Providerの加工部分にまかせることで問題解消できる。
StateNotifierProviderからStateNotifierProviderを呼ぶこと自体は公式も認めているだろうが、それは呼ぶ側も呼ばれる側も状態を更新するためであって、呼ぶ側は単にUI側のイベントを吸い上げて画面側のProviderを呼ぶラッパーのような使い方はおそらく公式の思想に合ってない気がする。
さいごに
ここら辺は作ってるときは正直何とも思わなかったし、むしろよい設計だと思ってすらいた。ただTest書く時に量が多くてめんどいなって思ったり、Providerのモック化するときにProvider間の結合がだるいなって思ったりすることで再考察するいい機会になった。
仕事の方は戻すのはほぼ無理だが、個人で書く分にはいくらでも手を加えられるので再考案して書いていきたい。