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やこちらのブログが大変参考になります。
{
id: "123",
author: {
id: "1",
name: "Paul"
},
title: "My awesome blog post",
comments: [
{
id: "324",
commenter: {
id: "2",
name: "Nicole"
}
}
]
}
{
"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モデル)となります。
コマンドモデルを書き換えると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アドベントカレンダーがら空きだしね!