Slide 1

Slide 1 text

Domain Driven reDux あるいは Redux as CQRS pixiv Inc. f_subal 2018/12/07

Slide 2

Slide 2 text

2 誰 ● pixivFACTORY フロントエンド ● 巨大Reactコンポーネント建造業 ● グッズ制作画面とか作ってます @f_subal

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

4 グッズの種類 グッズの仕様 テンプレート 材質 ページ レイヤー グッズの バリエーション アイテム

Slide 5

Slide 5 text

ドメインロジックが厚い フロントエンドを作っていく話 5

Slide 6

Slide 6 text

● Redux は ただのクライアントサイド CQRS だよ ● C と Q をドメインごとに切ると捗るよ ○ けど純粋な ducks パターンはきついよ ● ドメインモデルはプレーンな json と純粋関数で持つと良いよ ● ある程度 Redux 習熟してる人向けです 6 話すこと

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

● みんな大好き状態管理層(強いグローバル変数) ● 単方向データフローと相性がいい ● フロントエンドでCQRSするやつ ○ そのための pub / sub と middleware の仕組みを提供するライブラリ ○ https://speakerdeck.com/yamatatsu/reduxdebizinesurozitukuwogorigorishu-ku ?slide=12 8 Redux ってなんだっけ

Slide 9

Slide 9 text

9 http://krasimirtsonev.com/blog/articl e/my-take-on-redux-architecture

Slide 10

Slide 10 text

10 ThunkAction ( ≒ C 更新系) Selector ( ≒ Q 参照系) Action (ドメインイベント) http://krasimirtsonev.com/blog/articl e/my-take-on-redux-architecture

Slide 11

Slide 11 text

● CQRS とは ○ Command(更新系)と Query(参照系)で実装を分離しようぜっていう考え ○ 同じエンティティでも更新系と参照系では関心が異なるという前提に基づく ● flux は「データが単方向に流れる」という点から説明されることが多いが ○ 実は Store の更新と参照を別のフローに分離したという点が結構重要 11 Redux as CQRS

Slide 12

Slide 12 text

Action 12

Slide 13

Slide 13 text

● flux における Action には2つの意味がある ○ Domain Event を発行する ActionCreator ○ UseCase としての Action( ex: redux-thunk, redux-saga, redux-observable ) ● アプリケーション内で「起こった出来事」を表す ○ ので、過去形で命名するようにしている ● UseCase としての Action は、複数の Domain Event を投げることがある 13 Action = 更新系

Slide 14

Slide 14 text

● Action = アプリケーション内で「起こった出来事」 ○ ので、過去形 or 完了形で命名するようにする ○ 命令 とか 処理を表す名前にすると、実装者が責務を誤解する ● ◎ → USER_PROFILE_LOADED // よい ● △ → FETCH_USER_COMPLETE // まだマシ ● × → FETCH_USER_BY_ID // やめて 14 Action = 起こった出来事

Slide 15

Slide 15 text

15

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

● 以下、暗に redux-thunk の使用を想定する ● 「Ajax リクエストをして成功したら USER_LOADED を、失敗したら USER_LOADING_FAILED を dispatch したい」みたいな、複数の action を発行しうる一連 の手続き ● CQRS の C( Command )の部分 ● Action との統一の都合上こちらも過去形で命名している 17 ThunkAction = ユースケース

Slide 18

Slide 18 text

18

Slide 19

Slide 19 text

● Redux 始めた人が最初に悩むやつ ● Action と Reducer に何をどこまでやらせるか曖昧になりがち ● ベストプラクティスとして reducer には複雑な処理を書かない、と言われるが ○ 言われるだけでなんでダメなのか分かってない人もいると思う ○ 各種ドキュメントはあんまりこの辺教えてくれない 19 Action と reducer の責務問題

Slide 20

Slide 20 text

● case SET_NEW_ITEM: みたいな命名をしていると、あたかも reducer で副作用を起こし ていいかのような誤解が生じる ○ SET_* 系の命名マジでやめたほうがいいと思う ● case NEW_ITEM_LOADED: と命名すれば、ロード自体はもう終わっていて、良いから後 は store に反映して、という理解になりやすい(たぶん) ● 「アプリケーションで起こった出来事に反応して自身を更新」という構造にする 20 Action と reducer の混同は命名で矯正できる よ(過激派)

Slide 21

Slide 21 text

21

Slide 22

Slide 22 text

● 特定のドメインで起こりうる出来事の一覧を enum + ActionCreator として定義 ○ 過去形で表現されるドメインイベントの一覧 ● 複数の Action を発行することのできるユースケースを Thunk Action として定義 ○ 統一のため thunk も過去形で命名している ○ ex) userProfileRequested() という thunk を投げると、USER_PROFILE_LOADED が dispatch される ● reducer は Action の発生を見守る(何か起きたら自身を更新する) 22 factory における Action + Reducer

Slide 23

Slide 23 text

参照系 23

Slide 24

Slide 24 text

● 前提として Redux の Store はでかい ● アプリケーションドメインが全部入った 1枚のJSON 24 Selector = 参照系

Slide 25

Slide 25 text

25 グッズの種類 グッズの仕様 テンプレート 材質 ページ レイヤー グッズの バリエーション アイテム

Slide 26

Slide 26 text

● コンポーネントがこのでっかい JSON から値を引く際、Store の内部構造を知らなくても すっと引ける仕組みが欲しい ● これが selector(実装には自動メモ化機能などがついた reselect が使われる) ● CQRS の Q の部分(参照系のビジネスロジック) 26 Selector = 参照系

Slide 27

Slide 27 text

27

Slide 28

Slide 28 text

ドメインごとに切る 28

Slide 29

Slide 29 text

● action, reducer, selector をどういう構成で置くかはプロジェクトごとに結構違う ● 一番良く見るのは次の形式 /actions user.ts /reducers user.ts /selectors 29 redux のディレクトリ構成

Slide 30

Slide 30 text

● ファイル移動が多くなりがち ● 「俺は user が規約に同意したことを true にしたいだけなのに、なんでディレクトリ 4つもま たいでるの、アホなの」問題 ● 対抗策としていわゆる ducks パターンという設計が出てきた 30 /actions 形式の問題

Slide 31

Slide 31 text

● 同じドメインの action, reducer, selector を1ファイルにまとめる設計 ● 各 ducks は完全に中に閉じる(基本相互に関係しあわない) /modules /user duck.ts /item duck.ts 31 ducks パターン

Slide 32

Slide 32 text

32 https://webbibouroku.com/Blog/Article/redux-ducks

Slide 33

Slide 33 text

● ducks パターンだと流石に閉じすぎててつらいということから出てきた(多分) ● ドメインごとに切る点は踏襲してるが、ファイルは別々 /domains /user action.ts reducer.ts selector.ts 33 re-ducks パターン

Slide 34

Slide 34 text

● だいたい re-ducks パターンの変形 ● 各ドメインは相互に干渉し会える ○ 「Product の thunk が Specification の actionを発行」とかを許容している ○ 純粋な ducks パターンで 1 ファイルにまとめる と ↑ がやりずらくなるのでやめた 34 factory のファイル構成

Slide 35

Slide 35 text

● 実はリニューアル前の旧エディタは /actions のようにディレクトリを切っていた ● 結局の所 Store 内の 1個の値を更新するのにファイル移動が異常に多くて萎えた ● 新しい設計でもファイル移動は発生するが、同じドメインなら同じフォルダなので面倒さは 少ない ● ドメインごとにフォルダを切ったことでコンテキストが境界づけられたのも良かった ● 結論おすすめです 35 factory のファイル構成

Slide 36

Slide 36 text

ところで 36

Slide 37

Slide 37 text

● 型定義 + 関数 + 定数 の集まり ● みんなよく types.ts とか userUtil.ts とか constants.ts とか作るけど、あれを1ファイルに纏 めたもの ● action と selector、あるいはコンポーネントからも 呼べる共通関数とかおける ● 別の機会にこれだけで話したい …かも 37 model.ts って何や

Slide 38

Slide 38 text

38

Slide 39

Slide 39 text

まとめ 39

Slide 40

Slide 40 text

● Redux は ただのクライアントサイド CQRS だよ ● C と Q をドメインごとに切ると捗るよ(けど純粋な ducks パターンはきついよ ) ● ドメインモデルを純粋な形で持つようにすると良いよ 40 まとめ

Slide 41

Slide 41 text

Redux as CQRS あるいはドメイン駆動 Redux pixiv Inc. f_subal 2018/12/07