2018-09-30
Re-ducks というディレクトリ構成のベストプラクティスについてまとめる。Re-ducksパターンを使うことで、React + Redux を用いた開発がよりメンテナンスしやすいものになる。
名前からもわかるように、Re-ducks は Ducks というパターンをベースにしている。
Ducks パターンが解決すること: actionType、action、reducerが散らばっててつらい
結局のところ actionType、action、reducer は密結合なので、ducksパターンではこれら3つを1つのファイルにまとめる。
// widget.js
// Actions
const LOAD = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';
// Reducer
export default function reducer(state = {}, action = {}) {
switch (action.type) {
// do reducer stuff
default: return state;
}
}
// Action Creators
export function loadWidgets() {
return { type: LOAD };
}
export function createWidget(widget) {
return { type: CREATE, widget };
}
export function updateWidget(widget) {
return { type: UPDATE, widget };
}
export function removeWidget(widget) {
return { type: REMOVE, widget };
}
// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
}
ducksパターンではこのようなファイルを module
と呼ぶ。
module
を作る上で、いくつかのルールがあるのでこれを守る。
reducer()
という名前の関数を export default
しなくてはならないexport
されなくてはならないnpm-module-or-app/reducer/ACTION_TYPE
という形で持たなくてはならないUPPER_SNAKE_CASE
の形で export
できる。一般的な Redux のディレクトリ構造と、ducksパターンを比較すると以下のようになる。
// 一般的なディレクトリ構造
actions
├ articles.js
├ comments.js
└ users.js
reducers
├ articles.js
├ comments.js
└ users.js
types
├ articles.js
├ comments.js
└ users.js
// ducksパターン
modules
├ articles.js
├ comments.js
└ users.js
もともと関数の種類によって分類されていたファイルが、ducksパターンでは機能ごとに分類されるようになった。バラバラになっていたファイルがまとまり、とてもわかりやすい。
Ducksパターンおかげでディレクトリ問題が解決されたかに思われた。しかし開発の規模が大きくなるにつれて、しんどさが増していく……。
アプリが中・大規模になると、1つに集約された module
ファイルが読みづらく、メンテナンスしにくいものになってしまう。この問題へのアプローチとして Re-ducksパターンが生まれた。
Re-ducksパターンが解決すること:ducksパターンにおける module
がだんだん肥大化していってつらい
Re-ducksパターンでは以下のようなディレクトリ構造になる。
ducks
├ articles
│ ├ index.js
│ ├ types.js
│ ├ actions.js
│ ├ reducers.js
│ ├ operations.js
│ └ selecors.js
│
├ comments
│ ├ index.js
│ ├ types.js
│ ├ actions.js
│ ├ reducers.js
│ ├ operations.js
│ └ selecors.js
│
└ users
├ index.js
├ types.js
├ actions.js
├ reducers.js
├ operations.js
└ selecors.js
Re-ducksパターンでは機能ごとにディレクトリを分け、その中で関数の種類ごとにファイルを分ける。つまりducksパターンの「機能ごとの分類」と、一般的なReduxの「関数の種類ごとの分類」をいい塩梅で組み合わせている。
ちなみにディレクトリ分けの基準になる「機能」は、見た目で分類するよりもデータで分類した方が無理がないとのこと。
Re-ducksパターンでは、ducksパターンと比べてファイルの数がかなり多い。しかし、それぞれのファイルが果たす役割が明確なので思いのほかシンプル。
ducksパターンにおける module
ファイルは、Re-ducksパターンの index.js
に当たる。ここで他のファイルをまとめ上げ、利用する際のインターフェースにする。作成時には、ducksパターンと同じルールを守らなくてはならない。
types.js
、actions.js
、reducers.js
の3つは一般的なReduxのディレクトリ構造でも別々のファイルとして作成される。ducksパターンでは1つの module
ファイルとしてまとめられたが、Re-ducksパターンでは結局バラバラのファイルになった。
Re-ducksパターン固有のファイルとして、operations.js
と selectors.js
の2つがある。これらのおかげで、state をシンプルに保ったまま複雑な処理を実装できる。
Operations
Selectors
Re-ducksパターンを最初に目にしたときはファイル数の多さに戸惑った。しかし、ducksパターン → Re-ducksパターンの流れを把握したら腑に落ちた。
Re-ducksパターンの実装例としては以下の example がとても参考になった。文字であれこれと語るより、コードを追った方がすんなり理解できるかも。
また、Re-ducksの発案者が書いた記事がmediumにあるので、迷った時にはこれに立ち返れば間違いがない。