redux-aggregate v2 - Action / Reducer の分割 -

redux-aggregate が v2 になりました。主な変更点は以下の通りで、v1 からの Breaking Change はありません。アプリケーションスケールアップの際に効果が期待出来る変更を追加しています。

- createActions の追加
- Aggregate.subscribe の追加
- subscriptions 概念の追加

これからは createAggregate で生成された ボイラープレートでも、reducer / action が多対多の関係を持つことが可能になります。もちろん新しく追加されたAPIも、必要最小限の型定義から推論型を導出します。

# createActions

従来の ActionCreator 定義を確認してみましょう。ActionType と payload が結合していますね。

```actions/timer.js
function tick (message) {
  const date = new Date()
  if (message !== undefined) return { type: 'TIMER_TICK', pyload: { date }}
  return { type: 'TIMER_TICK', payload: { date, message }}
}
```

ここから ActionType を取り除き、payload だけを return するものを actionSrc と呼んでいます。**in/out があるだけのただの純関数であることが分かります**。

```actions/timer.js
// @ ActionSources

function tick (message) {
  const date = new Date()
  if (message !== undefined) return { date }
  return { date, message }
}
export const TimerAC = { tick }
```

`createAggregate` と同様に、ActionSources / namespace を付与し `createActions` を call します。

```store.js
import { createActions } from 'redux-aggregate'
import { ActionSources } from 'path/to/actions/timer'

const Timer = createActions(ActionSources, 'timer/')
const {
  types,    // ActionTypes
  creators, // ActionCreators
} = Timer
```

`createActions` では reducerFactory は生成されません。

# Aggregate.subscribe

createAggregate で生成された Aggregate インスタンスに、subscribe というメソッドが生えました。このメソッドで上述の Actions を購読します。購読に応じた変更処理は後述の **subscriptions** に書いていきます。

```store.js
import { TimerActions } from 'path/to/actions/timer'
import { TodosMutations, TodosSubscriptions } from 'path/to/models/todos'
export const Timer = createActions(TimerActions, 'timer/')
export const Todos = createAggregate(TodosMutations, 'todos/')

Todos.subscribe(
  Timer,  // Actions or Aggregate
  TodosSubscriptions.Timer  // Subscriptions
)
```
一般的な subscribe とは少し異なり、これは reducerFactory を拡張するものです(うっかり多重 subscribe したところで、違いはありません)。

# subscriptions

mutations が定義されているモデルファイルに、購読マップ（subscriptions）を定義します。見た目は mutations とそっくりですが、ActionType / ActionCreator を生成しません。subscription 単体は、従来の switch文で選り分けられた先の reducer と等価です。

注意しなければいけないのは、subscription は規約を守らなければ、型エラーが発生します。**一体何の規約かというと、それは開発者自身が定義したメソッド名・payload に則るということです。**

example で示しているとおり、TimerActions の tick というメソッド名・メソッド戻り値を第2引数にとることが subscription関数に期待されます。第1引数は `TodosState` を指します。**modelファイルに定義される全関数は、所属する state を中心に回ります。**

```actions/timer.js
// subscription の名称は tick に制約される
function tick (message) {
  const date = new Date()
  if (message !== undefined) return { date }
  return { date, message }
}
// subscription の第二引数は { date } か { date, message } に制約される
```

```models/todos.js
const TodosSubscriptions = {
  Timer: {
    tick(state, { date, message }) {
      if (message !== undefined) {
        return { ...state, date, messageFromTimer: message }
      }
      return { ...state, date }
    }
  }
}
```
他のメディアでも発信していますが、このライブラリの最大の利点は、**「アプリケーションの核が純粋な言語仕様のみで確立されていること・ Redux の概念が介入していないこと」**であり、コード・付随テストの寿命を伸ばす大事なことだと筆者は考えています。

# 利用シーン

概念が増えたことで少しややこしくなりましたが、**今回の追加機能は必要に応じて導入、スケールアップのシーンで役立てて下さい**。はじめは 単体Aggregte の関心事であった Action が、他のAggregate の関心事となったタイミングで利用すると良いかと思います。

Aggregate.subscribe は 他Aggregate の Action も購読出来ます。2つのAggregate の関心ごとになった際には、下流が上流を購読すれば、それで十分です。それより多くの関心事となった場合や、当初から多くの関心事であると想定された場合は、`createActions` を使って「Action のみの集約」を作ってください。

Action を全く持つ必要のない集約の場合、`createSubscriber`を使って「Reducer のみの集約」を作ってください。[example ->](https://github.com/takefumi-yoshii/redux-aggregate/blob/master/examples/with-saga/src/models/summary.ts)



公式Document を公開しました https://redux-aggregate.js.org/