Redux ToolkitのcreateSliceはちょっとReduxぽくない処理をしてる。その背景にある哲学は何か。
特徴: ActionとReducerの凝集/密結合
Reduxは「ActionとReducerはN:M対応、互いに疎結合」という哲学1,2.
しかしRTK.createSliceはCaseReducerからAction/ActionCreatorを自動生成する。
つまり「ActionとReducerは1:1対応」にしてしまう.
これはぱっと見、Reduxの哲学に反している(そう感じている人のコメント link1, 2, 3).
問題意識: 機能的凝集を疎結合にするな
Reduxを採用する規模の状態管理では、2種類のActionが現れる.
(ここでの命名は私)
- cross-domain Action: 複数のdomain3にまたがるAction
- Action:Reducer = N:M。Eventなイメージ
- in-domain Action: 1つのdomainにのみ影響4
- Action:Reducer = 1:1。RPCなイメージ
- Action設計段階から処理(reducer)に1:1で結びついている
Redux哲学とin-domain Actionの関係が問題。
ActionとRecuder (イベントと処理) が設計から結びついている == 機能的に凝集している。
なので凝集してるものをRedux-wayでむりくり疎結合にするのは凝集度を下げちゃう過剰な抽象化、KISS原則の違反。
Ducksパターンはこれに対する回答の1つ5。
考慮点: cross-domainあってのRedux
全部がin-domain ActionならReduxいらない。
でもある程度の規模になるとcross-domainも混在してくる.
in-domain Actionをcross-domain Actionとして利用したい日も出てくる.
この混在をどうするか考慮が必要.
解決策: in-domainは凝集させてcross-domainは疎結合
Redux ToolkitのcreateSliceは両タイプのAction/Reducerを扱う.
- in-domain
- CaseReducerに基づいたAction/ActionCreator生成 6(機能的凝集)
- cross-domain
Actionというメッセージパッシングの枠組みは保持(== Redux)。
その上でin-domainな処理はAction自動生成&Reducerと同じ位置に保持(Ducksライク8, 9, 10)にすることでActionを意識させない作りにする.
cross-domainは今までどおり使えるようし、自動生成のActionもcross-domainへ転用できるような作り (1:1に見えるけど1:Nに使える).
部分部分で必要な抽象度を制御してKISS原則を守り、usefulnessを上げた(「過剰な抽象化による大量のボイラープレート」の除去)。
Refs
- reduxjs/redux-toolkit - Feature discussion:
createSlice
behavior #91- selectorの議論がメイン
- reduxjs/redux-toolkit - Discussion: Roadmap to 1.0 #82
-
‘One of the key concepts of Redux is that each slice reducer “owns” its slice of state, and that many slice reducers can independently respond to the same action type.’ Redux Toolkit↩
-
“caveats to keep in mind: Actions are not exclusively limited to a single slice. Any part of the reducer logic can (and should!) respond to any dispatched action.” Redux Toolkit↩
-
== slice == FluxStore ↩
-
“95% of the time, it’s only one reducer/actions pair that ever needs their associated actions.” Ducks ↩
-
“for these pieces to be bundled together in an isolated module that is self contained” ↩
-
“the point of createSlice is … automatically generate the actions and types so that they don’t have to.” Redux Toolkit issue ↩
-
“I do want createSlice to support … that includes listening for action types that aren’t defined by this slice.” Redux Toolkit issue ↩
-
‘the result object is conceptually similar to a “Redux duck” code structure.’ Redux Toolkit ↩
-
‘createSlice does work as a “ducks” generator just fine.’ Redux Toolkit issue ↩
-
‘to some extent createSlice does edge kinda close to the “Redux modules” concept’ Redux Toolkit issue ↩