Runner in the High

技術のことをかくこころみ

ドメイン・イベントについて&Ruby製のよさげなPub/Subインターフェースgemまとめ

Rubyで特にRailsを使う際に「特定のドメインの変化によって別の処理の実行をトリガする」みたいなケースでは大抵の場合コールバックが使われる。 しかし、ぶっちゃけた話コールバックはかなり結合度の高いコードになってしまいがちで、実装的にスケールさせるためにはドメイン・イベントを使うほうが健全であると言える。 martinfowler.com

以下の記事はActiveRecordのコールバックがどのようなときによろしくない感じになるかを説明しており、非常に参考になる。一言で言うと、コールバックを使うことモデル自体に副作用が埋め込まれてしまう。一方でドメイン・イベントを使うことで、副作用がなにをするものなのか(メールを送る、外部のmBaaSを更新する、モニタリングサーバへメトリクスを送信する、等)を意識しないでよくなり、疎結合になる。 techracho.bpsinc.jp

さて、ドメイン・イベントの仕組みを実装する際、もちろんオンメモリなオブザーバ・パターンの仕組みを実装してもよいが、それは開発環境でしか使えない。プロダクションだと大抵APサーバは複数台構成のはずなので、Redisあたりにキューして複数の異なるAPサーバ上のサブスクライバがイベントを拾えるようにしたい。こうしておけばQueue-based Load Levellingもできるようになるので負荷分散もできてスケールする。

もっというならGCPのときはCloud Pub/Subで、AWSのときはSQSを使ったりできるようなプラガブルな感じになっていると最高だ。そんなものを求めていくつか探してみた。

dry-rb/dry-events

github.com

  • みんな大好きdry-rbシリーズのPub/Subインターフェイス
  • おそらく今回紹介するgemの中で一番シンプル。
  • Dry::Events::Publisherというモジュールをミクスインしたクラスを作ってサブスクライバを定義すると、そのクラスに対してイベントをパブリッシュできる。これだけ。
  • 開発が比較的活発なのもいい

kris.leech/wisper_next

gitlab.com

  • 元々はwisperというPub/Subインターフェイスgemを作っていた作者による新しいライブラリ
  • WisperNext.publisherWisper.subscriberというふたつのモジュールが用意されておりそれらをミクスインしたクラスを作るスタイル
  • パブリッシャクラスに対してsubscribeメソッドでサブスクライバクラスを追加していく。
  • 特定のプレフィクスを持つイベントのみをサブスクライブできる機能もある。
  • サブスクライブをする際に同期/非同期を選択できる。

kris.leech/ma

gitlab.com

  • 上と同じ作者のPub/Subインターフェイスgem。内部的にはwisper_nextが使われている。
  • ライブラリの説明に "events as first class citizens." と書かれている通り、wisper_nextとの違いはパブリッシュ/サブスクライブするイベントをクラスとして定義できること。Ma::Eventクラスを継承したクラスをイベントとして扱うことができる。
  • それ以外にはMa.publisherMa.subscriberというモジュールが用意されており、それぞれをミクスインしたクラスを作るスタイルで特に目新しいものはない。

edisonywh/rocketman

github.com

  • 今回紹介するgemの中で一番重厚かつ拡張性が高い。
  • Rocketman::ProdcuerRocketman::Consumerのふたつのモジュールがいて、それぞれをミクスインしたクラスでemitとかon_eventみたいなメソッドが使える。まあわかりやすい。
  • Redis上のイベントもサブスクライブできるアダプタが用意されている。現状はRedisだけだが、Registryという仕組みが用意されているので自前でカスタムの外部サービス用アダプタを作ることも可能。
  • 初期設定では発行されるイベントがキューとしてメモリ上に保存され、Rocketmanのスレッドワーカーが処理していく仕組みになっている。これだとアプリケーションが死んだときキューをロストするので、もしキューを永続化したければRedisをストレージとして使うことができる。
  • 作者によるとRedis PubSubやKafkaはそのあたりのミドルウェアをメンテしたりするコストがかかるが、Rocketmanを使えば実質アプリケーションレイヤで同じことができるので、すごく楽だよ、というのが売りらしい。

総括

アプリケーション層でPub/Subしたいだけならどれでもよさそう。もっとリッチに、イベントを永続化したり複数台構成で負荷分散とかに使いたい場合はrocketmanがよさそう。GCPとかAWSあたりのPub/Sub製品のアダプタも自前で作れそうだし。ただメンテされているのかどうかが若干気になるが...

Reactive Design Patterns

Reactive Design Patterns