この記事を書いた当時、MobXは余計な複雑さが隠蔽され、とても良く目に映りました。何よりReduxにはOOPが可能なモデルが無い、という当時のReduxに対する不満を解決してくれました。(今はReduxにもモデルを持ち込む手法を選んでいるので不満はありませんが)
直近、同僚に「Redux と MobX の選択はどうすれば?」と相談されたので、その日の返答内容をまとめてみました。
TODOサンプルだけでは測れない現実の課題
ライブラリのキャッチアップに、TODOや「+1」するカウンターで検証することはよくあると思います。ただし、これだけでは長期運用するプロダクトの検証には不十分です。 MobXは便利で簡単に始めることが出来るため、Storeを無作為に参照しあってエンバグしやすいコードが出来てしまいがちです。
Storeが数えるほどしかなく、機能も単純であれば原因を突き止めるのは容易です。しかしながら、現実は技術要件とビジネス要件が入り組みます。Store 同士のロジックが密結合してしまい、そこに依存するアプリケーションを組んでしまうと、技術負債の出来上がりです。
Storeが密結合してきた場合
Storeが密結合してきたら、Storeの結合点を外に逃すのが得策かと思います。複数の reaction を用意して、Storeとは切り離されたレイヤーで Observable な値に対してマッピングを行います。こうすることで、各々の Store は疎結合になり、ユニットテストも容易になります。
const storeA = new StoreA()
const storeB = new StoreB()
const storeC = new StoreC()
reactions({ storeA, storeB, storeC })
export default function ({ storeA, storeB, storeC }) {
reaction(
() => storeA.value,
value => { storeC.value = value * storeB.value }
)
reaction(
() => storeB.value,
value => { storeC.value = value * storeA.value }
)
}
難点は、Store が増えるほどトランザクションが追い辛くなることです。リファクタ・スケールにおいては、簡単な部類ではないと思います。普段から出来ることといえば、他の Store が別の Store の状態を直接参照しないようにすることぐらいです。
MobXにする境界
1.異なるプラットフォームでドメインモデルを共有したい
React + MobX と紹介されることが多々ありますが、両者は疎結合で MobX をドメインモデルとすることが可能です。別の view で再利用可能で、データバインディングフレームワークと比べ、ドメインモデルが切り離されています。
そのため react-dom・ReactNative でドメインモデルを共有することもなども容易です。もしこの点に魅力がなければ、Reactに固執する必要もないかもしれません。
2.Storeの密結合が起こりえない
json を取ってきて SPA で記事を表示するだけ、というユースケースの場合は、Store の結合は問題にならないかと思います。非SPAの場合も同様で、MobX が最適解だと思います。
3.とにかく早く ReactComponent を書き上げたい
Redux もそうですが、React とは疎結合なため、状態管理は変更可能です。ReactComponent はそのままで、状態管理だけ差し替えることが出来ます。「Store結合が複雑な大型改修が必要になったら、Reduxに乗り換える」ぐらいの心意気があれば、最初は MobX の方がシンプルに目指しているものが作れるかと思います。
【こんな案件だったら自分はMobXを選ぶ】
- エンドポイントによっては部分的にSPA
- メディア媒体・シンプルな投稿機能サイト
- 期間限定のプロモーションサイト
- 個人のブログ
Reduxにする境界
1.異なるプラットフォームでドメインモデルを共有したい
こちらもまた、Reactとは疎結合なのでドメインを切り離すことが出来ます。普通の使い方をすると、Reduxはデータソースの保持のみでドメインモデル不在ですが、immutable.js を導入すればOOPが可能なドメインモデルを持ち込めます。Reducer の switch 文 やボイラープレートが辛いと感じる方は、こちらを参考にしてみてください。私自身の直近業務では、よく見る3種のボイラープレートは一切 git commit していません。(それ相当のものはもちろんcommitしています)
2.モデル同士の複雑な密結合が予想される
redux-saga は複雑だと敬遠されがちですが、非同期処理だけのものではありません。上記の様な MobXStore 密結合問題と同様に、redux-saga上でモデル間のマッピングが行えます。
一番の特徴は、そのトランザクションの組み替えが容易なこと。async/await と使い勝手が同じなので、学習コストが無駄になることが無いです。
ActionType名称を変更しても、Objectでマッピングされているため、悩み事は最小限で済みます。(redux-saga 以外に redux-observable でも可能です)
3.書き捨てではなく、長期的に保守していきたい
先に述べたとおり、リファクタに強いです。後々の保守工数を鑑みても、長期運用やスケーラビリティはこちらに分があります。
【こんな案件だったら自分はReduxを選ぶ】
- 全エンドポイント SPA
- 機能豊富な WebBrowser アプリ
- 機能各種が密結合する ReactNative アプリ
- 複数人でのフロントエンドコード長期保守
最後に
- 作るもの・締め切りなどによって最適解は変わります。
- ボイラープレートが辛い、という消去法で MobX にするのはやめましょう。
- 少し工夫でボイラープレートは気にならなくなります。
- Store や Model が純粋であることは尊いです。テストが書きづらく感じたら、赤信号です。
- 両者のpros/cons を理解して最適解を選びましょう。
- もしかしたら、React以外の選択もあるかもしれません。