CQRS
redux
ReduxDay 1

reduxのstateを正規化してCQRSの恩恵を受ける土台を作る

2017reduxアドベントカレンダーの1日目が空いていたので、書いてみます。
よろしくおねがいしますー

書くこと

reduxのドキュメントのMotivationの頁にてCQRSに言及されています(微かに)
reduxのCQRSらしさを引き出してその恩恵を受ける方法を書いていきます。

CQRSってなんだ?

Command Query Responsibility Segregation
日本語だとコマンド、クエリ責任分離とかって訳されます。
コマンドは副作用が主目的の処理、クエリはデータを取ってくることが主目的の処理のことです。

マクロな話だと「データを書き換えとデータの取り出しは、サーバーもDBも分割してしまえ」という設計方式になります。
ミクロな話では「副作用を持つメソッドは戻り値を持つな、戻り値があるメソッドは副作用を持つな」という、コーディング原則(?)としても存在します。

ちなみに、fluxは(reduxも)生まれながらにCQRSです。
reduxは、CQRSとevent sourcingをリスペクトした関数型言語であるelmからインスパイア(というかパクった説も)されているので、この辺の勉強をすると設計がはかどります。

まずはstateをnormalize(正規化)する

reduxドキュメントには「stateは正規化しよう」と書いてあります。

jsonデータの正規化って?

「jsonデータの正規化とは?」という話はこのreduxドキュメントでも紹介されているnormalizrこちらのブログが大変参考になります。

非正規化されてるjsonデータ
{
  id: "123",
  author: {
    id: "1",
    name: "Paul"
  },
  title: "My awesome blog post",
  comments: [
    {
      id: "324",
      commenter: {
        id: "2",
        name: "Nicole"
      }
    }
  ]
}
正規化されてるjsonデータ
{
  "articles": { 
    "123": { 
      id: "123",
      author: "1",
      title: "My awesome blog post",
      comments: [ "324" ]
    }
  },
  "users": {
    "1": { "id": "1", "name": "Paul" },
    "2": { "id": "2", "name": "Nicole" }
  },
  "comments": {
    "324": { id: "324", "commenter": "2" }
  }
}

(どちらの例もnormalizrから拝借しています。)

normalizrとは?って話はすでに良き記事が出てるのでそっちとか本家のReadmeとかReduxのDocとか読んで頂ければと
Normalizrを使用したReduxの実装パターン

正規化されたデータはネスト構造が浅くなり、正規化以前にネストされてたデータへの参照(id)を持っています。
RDBの設計に似てると思います。

正規化されたデータの方が、書き換えが容易で、変更が多いデータに向いています。

CQRS的には

redux stateはCQRSで言うところのコマンドモデル(writeモデル)となります。
Screenshot from Gyazo
コマンドモデルを書き換えるとsubが反応してクエリモデルを書き換えます。

クエリモデルどこいった?

reselectにメモ化されたキャッシュ」がreduxにおけるクエリモデルに当たるかと思います。
reselectは本家でも紹介されていて、こちらのstackoverflowでもreduxのコミッターがおすすめしています。
日本語だとこの記事が詳しいです。

CQRS的には

実際のCQRSではコマンドモデルとクエリモデルを別々のストレージに記憶させますが、RDB一つを使って設計する方式もあります。その場合、テーブルviewをクエリモデルとします。
テーブルviewがキャッシュされるのと同じようにreselectもメモ化と言う形でキャッシュされるので、なんかそのへん意識して作られたんじゃないかなって思います。

クエリモデルは画面表示に特化させて、好き勝手やっていいです。jsonの形に特に縛りはないのでご自由に。

selector書くのめんどいよ

わかります。それでも書く価値がselectorにはあります。
それはstate、react間の腐敗防止層としての価値です。

selectorを書くことで、reactはstateの形を知る必要がなくなり、stateの形を知っているのは、reducerとselectorだけになります。これはアプリケーションが進化する過程でstateの形をリファクタリングしたくなったときに価値を出します。
またreact側の修正があった場合についても、stateの形を変更しなくても欲しい形のjsonを手に入れる事ができます。

以上の理由から、middlewareでstateを使う場合もちゃんとselectorを書いたほうが良いです。DDD話でいくと、selectorを用いてfactoryを作り、それを使って集約を構築する感じになります。
この辺はまた今度記事にできたら良いなって思います。

CQRSをもっと知りたい方はこちら

Greg Young流CQRS
CQRS Documents by Greg Young

実践ドメイン駆動設計にはCQRSやそれ以外のアーキテクチャについて、「ここにレイヤードアーキテクチャがあるじゃろ?」から始まり、なぜレイヤードアーキテクチャじゃないアーキテクチャが提唱されていったのかが説明されていて、ためになる本でした。

果たして幸せになったのか

stateを正規化しつつ、reactが非正規化データを受け取れるようになり、腐敗防止層も手に入れた。
しかし、もともとapiで受け取ったデータをそのままredux stateに突っ込んで、reactでもさほど困っておらず、かつAPIが大きく変更することもない、というプロジェクトでは「幸せになった気がせんな」という感じかもしれない。

reduxは、このCQRSとイベントソーシングによって、ユーザーイベント、外部API、redux storeの間に住まうドメイン世界をクリーンにかつ外界の依存関係を切り離して設計することができます。クリーンアーキテクチャ、イベント駆動アーキテクチャなどと相性が良く、ドメインにある程度の重さがあるとき、その複雑化を軽減させることができます。

あとがき

質問やご意見があれば頂けると喜びます。

ドメイン駆動設計、ドメインイベント、イベント駆動アーキテクチャ、イベントソーシングなどをreduxに当てはめる話も書けたら書こうかと思います。
reduxアドベントカレンダーがら空きだしね!