CQRSはESであるべき?SSでも可能?

ESとSSがどういう場合に適用するとよいかみたいな話をした。CQRS/ESを知らない方は1)を資料を、発端の話題は2)の資料をみてください。 1) CQRS+ES(再)入門: https://speakerdeck.com/j5ik2o/cqrs-plus-es-zai-ru-men 2) CQRSはEvent Sourcingなしで実現できるのか?: https://speakerdeck.com/j5ik2o/cqrshaevent-sourcingnasideshi-xian-dekirufalseka?slide=7
2
加藤潤一(かとじゅん) @j5ik2o

3ヶ月遅れで発見…。これはESなしでは現実的ではないという主張でした。 twitter.com/cero_t/status/…

2020-06-14 03:12:46
Shin Tanimoto / CERO-METAL @cero_t

「CQRSはESなしで実現できるのか」って、そりゃできるでしょ、逆は辛い知れんけど、ってパッと思ったけどどういう切り口で話すのか興味深い。 twitter.com/masuda220/stat…

2020-03-30 14:02:51
加藤潤一(かとじゅん) @j5ik2o

想像してみてください。CQRSでESを使わずに、CのCRUDして、QもCRUDするは、CとQを繋ぐためにキューを配置するので、必ずダブルコミットになります。避けたほうがよいです。スターバックもダブルコミットしないので避けたいところです。enterpriseintegrationpatterns.com/ramblings/18_s…

2020-06-14 03:19:38
加藤潤一(かとじゅん) @j5ik2o

ダブルコミットを避ける方法としてはCに状態を書いたとしてもドメインイベントも同一Txで書き込んで、そのあとにそれをbinlogを読むか、SELECTしてQ側に連携する方法。もうこれESだからね。ダブルコミットを避けようとすると結局ESになってしまうのですよ。

2020-06-14 03:23:04
加藤潤一(かとじゅん) @j5ik2o

スターバック→スターバックス

2020-06-14 03:33:38
Shin Tanimoto / CERO-METAL @cero_t

@j5ik2o あれれ、、CQRS部分だけ論ずると、RDBのC/U/Dは通常テーブルで、Rはマテビュー、だとしても、これはある種のCQRSと呼べるのではないですか? (逆にCQRSせずにESするのはパフォーマンスが出なさすぎるとは思いますけども)

2020-06-14 04:17:24
がくぞ @gakuzzzz

QのCUDというのはどういう概念なのだろう?CとQが適切に別れてるならQにCUDは起きない様な……? twitter.com/j5ik2o/status/…

2020-06-14 04:45:34
加藤潤一(かとじゅん) @j5ik2o

@cero_t Rがマテビューだけで解決できる単純なケースならいいですね。責務から考えてC側のドメインオブジェクトはクエリ(リード)のことを考慮せずに内部値表現を作ってしまうのではないか。値の加工はメソッドでやるので、RMUなどで事前変換することになる気がします。 speakerdeck.com/j5ik2o/cqrshae…

2020-06-14 09:18:02
加藤潤一(かとじゅん) @j5ik2o

@gakuzzzz 書き方間違えました。QのCUDというのは、RMUでのCUDでした。C/Qの中間ですね。

2020-06-14 09:21:24
加藤潤一(かとじゅん) @j5ik2o

スライドのタイムスタンプはよくない例かも。いいたいことは、ドメイン固有の内部値がドメインのメソッドを通して表現が変わる場合に事前変換が必要になる。表現の選択はQに依存するけど、加工や変換はCに依存するケースですね。よくありそう…。

2020-06-14 10:02:51
がくぞ @gakuzzzz

@j5ik2o なるほどなるほど。RMUがマテビューで実現できる場合、トランザクションの問題はマテビュー側が解決してくれるので、その場合はESではないCQRSが成り立つ感じですかね。それでもESではないCQRSは無理というのは「マテビューで実現できる」仮定が現実的には不可能という様な主張なんでしょうか?

2020-06-14 11:39:59
Shin Tanimoto / CERO-METAL @cero_t

@j5ik2o ふむふむ、マテビューとかでは対応しきれず、現実的にはもっと加工とかが必要ですね。 たとえばステートソーシングでlast updateを打っておいて、1分ごとのバッチで「過去2分以内に更新されたデータをリードして加工して保存」すれば、これはCQRSではない?

2020-06-14 11:50:10
加藤潤一(かとじゅん) @j5ik2o

@gakuzzzz はい。そんな感じです。問題はC側のドメインオブジェクトが持つ内部値がそのままDBにライトされるとマテビューでリードモデルに表現するときに困ると思います。

2020-06-14 11:52:25
加藤潤一(かとじゅん) @j5ik2o

@gakuzzzz たとえば、C側の入社日の内部値がEPOCHだけどQ側では文字列表現の場合とか。多くの場合JoinedDate.toDateStringなどのドメインの振る舞いを通して使える型に変換・加工しますね。これをQ側でやってしまうとCQRSした意味がなくなってしまうと思っています。というのが基本的な考え方かなと。

2020-06-14 11:54:51
がくぞ @gakuzzzz

@j5ik2o なるほどなるほど。RMUとQ側のプレゼン層との分けみたいなのが悩ましくなってきました。例えばの話だと、C側はepockでQ側は文字列だとしても閲覧者のTimeZoneに合わせて表示したいからRMではepockのまま、だけどRMでは最大値だけあればいいからCQ分離する意味があってRMUでmaxとりたい的な

2020-06-14 12:06:36
加藤潤一(かとじゅん) @j5ik2o

@cero_t ですね。VO内部の値はVOがカプセル化しているので外からは解釈できない。別の型の変換・加工もVOが握っていたらQ側は手出しできないので、事前にQ側に読める形式に変換してねとなります。 SSでlastupdateのパターンですね。ちょっと考えます。

2020-06-14 12:07:49
加藤潤一(かとじゅん) @j5ik2o

@gakuzzzz DbCの観点から言えばVOに断り無く内部値を利用する方が悪いので、TZ依存形式ならjoinedDate.toEpocTimeというように契約を守って値をもらってQ側に取り込まないといけないと思います。ドメインの契約の境界面を冒してはならない、ちゃんとインターフェイスとプロトコルを介してください的なやつですね

2020-06-14 12:12:24
加藤潤一(かとじゅん) @j5ik2o

@cero_t SS+lastupdateの場合。C側のお更新された最新レコードを取得してQ側のリードDBをInsertOrUpdateですね。CQRSにはなると思います。

2020-06-14 12:17:50
がくぞ @gakuzzzz

@j5ik2o その契約というのがちょっと良くわかりませんでした。Q側の閲覧者が何人いてどこから来るのか、は逆にC側の責務としては関知しないので、必要なtimezone分を全て変換してQ側に取り込ませるというのは無理だし責務が分離できてないような?

2020-06-14 12:17:54
加藤潤一(かとじゅん) @j5ik2o

@cero_t ただ、スレッドの内部のメッセージが1000件ある場合は更新があるたび、まるっとリードDBを置き換えることになりますね。スレッドとメッセージを別別のタイミングでは書けませんね。メッセージの集合はスレッドのTxに入るので一緒に書くはめになります。かなり非効率ではないかなと。

2020-06-14 12:19:01
加藤潤一(かとじゅん) @j5ik2o

@cero_t あ、スレッドとメッセージの例は blog.j5ik2o.me/entry/2020/06/… こちらの例の話ですね。

2020-06-14 12:19:44
加藤潤一(かとじゅん) @j5ik2o

@cero_t スレッドとメッセージが一つの塊と考えた場合、RDBなら同じTxでQ側のリードモデルに書かないとおかしなことになりますね。まぁ、モデリングとしてもととも別にするなら別別に書けますけどね。一緒の境界と考えると,SS+lastupdateのまるっと更新は効率が悪いです。ESならそういうことはおきません。

2020-06-14 12:21:46
加藤潤一(かとじゅん) @j5ik2o

@cero_t ESではコマンドごとにThreadCreated→MessageAdded, MessageUpdated→…と差分の変更内容を表すイベントが流れてくるので、それをSQLなどの差分I/O命令に返還して実行するだけですね。例えばAccountRenamedならそのアカウントの名前をUPDATE文で更新するだけです。他のカラムの更新は不要です

2020-06-14 12:24:49
加藤潤一(かとじゅん) @j5ik2o

@gakuzzzz うまく表現できてなかったですがw TZ分の事前変換は無理がありますね。 仮に内部値がEPOCHであってもなくてもtoEpocというメソッドで変換してQ側で時刻文字列にするんじゃないかなという感じ。この場合はフォーマットはQの責任になりますが。ということで、QはCの内部表現に関与できないという感じ。

2020-06-14 12:33:36
Shin Tanimoto / CERO-METAL @cero_t

@j5ik2o ちょっと前提の確認なんですけど、スレッドの更新があったときに、メッセージ側で1000件のアップデートをしなくてはならない、というのはどういう情報をアップデートする想定なんですか?

2020-06-14 12:36:29
Shin Tanimoto / CERO-METAL @cero_t

@j5ik2o あ、ごめん、分かりました。 C側の1000件を更新するんじゃなくて、Q側の1000件を更新しなきゃいけないって話ですね。

2020-06-14 12:38:13