Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Event is what happens

Event is what happens

Kaigi on Rails 2026でのトークです

Avatar for MOROHASHI Kyosuke

MOROHASHI Kyosuke

October 16, 2026

More Decks by MOROHASHI Kyosuke

Other Decks in Programming

Transcript

  1. me • Rails application developer at SMS Co, LTD. •

    Love Ruby, Rails and XP. • Past Kaigi on Rails Talks available on YouTube & SpeakerDeck @moro ( • dynamic! • Identifying User Identity • Simplicity on Rails )
  2. Summary • Structured Event Reporting in Rails got me thinking

    about “events” • An event can represent something important that happened in our service • For me, understanding our service through its events feels right
  3. Events: • Structured Event Reporting (SER) • Event entities in

    relational database design • Event-Driven Architecture (EDA)
  4. Event entities in relational database design “ •• イベントエンティティを見出すとできること :

    時 存在 • 名前 持 。 扱 「時」 • 有効 • 存在 、存在 期間 <登録> 扱 「時」 表現 。 違 ドメインで大事な「登録したコト」をコード上に表現できる 、 瞬間 刻印 、 : ⾏為。 時起 <記録> •繰 返 ⾏ • 例) ⼊荷、発注、etc ) 持 。 • 時刻 ( 使 ⾏ • 多 場合、 ⾃体 ⾏為 名前 • 名前 ( UserMailer.registration_accepted(@user, @conference, at: Time.current).deliver_later # => #{@user.name}様、#{I18n.l(at)}に#{@conference}への参加を受け付けました UserMailer.registration_accepted(@registration).deliver_later • registration を見出していない場合、@userと@conferenceや、タイムスタンプを個別に持ち回る必要がある • @userはほんとに@conference登録している? 再チェックしたくならない? • タイムスタンプほんとにcurrentでいい? リトライするときどうする? • registration があれば、「登録したコト」をコード上でそのまま取り回せる )持 DB 設計勉強会 タワーズクエスト社 和田省二さんを先生にむかえた勉強会のメモ https://gist.github.com/sunaot/946062 • userとconferenceを辿れる。両者を辿れることは、DBの参照整合性制約でも支えられる • registrations.created̲at を登録日時とみなせる https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby
  5. EDA: Event-Driven Architecture • An architectural pattern that triggers processing

    when state changes occur • It loosely couples an event's occurrence with subsequent processing • Perhaps this is what we wanted from Active Record callbacks and observers
  6. These different uses of “event” started to look connected •

    Something important happened, • so we persist it in the database • so we log additional context • so we trigger subsequent processing
  7. ‣ Structured Event Reporting (SER) ‣ Event entities in relational

    database design ‣ Event-Driven Architecture (EDA)
  8. ‣ Structured Event Reporting (SER) ‣ Event entities in relational

    database design ‣ Event-Driven Architecture (EDA)
  9. What? • ActiveSupport::EventReporter reports structured events to subscribers • Call

    notify to report an event • Each subscriber implements emit, which is called when a matching event is reported • Accessible via Rails.event
  10. Rails.event.notify receipt_email = customer.email transaction do @order = Order.create!(customer:, receipt_email:)

    items.each { @order.order_items.create!(it) } end Rails.event.notify("order.placed", { order_id: @order.id })
  11. Event Object OrderPlaced = Data.define(:order) class ExternalCRMIntegrator def emit(event) order

    = event[:payload].order ExternalCRM.report_order(order) end end emitter = ExternalCRMIntegrator.new Rails.event.subscribe(emitter) { it[:name] == OrderPlaced.name } ... Rails.event.notify(OrderPlaced.new(order:)) # -> `ExternalCRMIntegrator#emit` called
  12. What is it for? • Typically, logging user activity when

    important events occur • The session ID associated with a conversion • The assigned A/B test variant • Information about the user's device fi • This information may not t our relational data model
  13. It may not t our relational model, but … •

    We still want to keep it because: • This data helps us operate the service and discover new insights • We may not need it initially, but it becomes more important as fi the service grows
  14. Before Rails 8.1 • In my experience, services often built

    their own reporting interfaces as they grow • A service often ended up with two or three of them • It looked to me like Shopify faced a similar problem
  15. Now we have Rails.event • We now have a standard

    interface! • We can report important events from the early stages of development • As we learn what we need, we can add subscribers
  16. How does it differ from ActiveSupport::Notifications? • Similar capabilities, different

    strengths • ActiveSupport::Notifications: instrument how a piece of code runs • ActiveSupport::EventReporter: report what happened, with context • For me, Rails.event offers a more direct way to express events̶and that got me thinking
  17. Recap: Structured Event Reporting • A pub/sub interface introduced in

    Rails 8.1 • Useful for logging information that does not t our relational model, especially as the service grows • With a standard interface, we can start reporting fi important events early
  18. ‣ Structured Event Reporting (SER) ‣ Event entities in relational

    database design ‣ Event-Driven Architecture (EDA)
  19. An event entity records something that happened in the domain.

    “ • • : 時 存在 • 名前 持 。 扱 「時」 • 有効 存在 • 、存在 <登録> 扱 「時」 期間 表現 。 違 、 瞬間 刻印 、 : ⾏為。 時起 <記録> •繰 返 ⾏ • 例) ⼊荷、発注、etc ) 持 。 • 時刻 ( 使 ⾏ • 多 場合、 ⾃体 ⾏為 名前 • 名前 ( )持 DB 設計勉強会 タワーズクエスト社 和田省二さんを先生にむかえた勉強会のメモ https://gist.github.com/sunaot/946062 https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby?slide=12
  20. A registration record lets us work with the fact that

    someone registered. イベントエンティティを見出すとできること ドメインで大事な「登録したコト」をコード上に表現できる UserMailer.registration_accepted(@user, @conference, at: Time.current).deliver_later # => #{@user.name}様、#{I18n.l(at)}に#{@conference}への参加を受け付けました UserMailer.registration_accepted(@registration).deliver_later • registration を見出していない場合、@userと@conferenceや、タイムスタンプを個別に持ち回る必要がある • @userはほんとに@conference登録している? 再チェックしたくならない? • タイムスタンプほんとにcurrentでいい? リトライするときどうする? • registration があれば、「登録したコト」をコード上でそのまま取り回せる • userとconferenceを辿れる。両者を辿れることは、DBの参照整合性制約でも支えられる • registrations.created̲at を登録日時とみなせる https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby?slide=21
  21. We can enrich that record and link subsequent events to

    it. 続: イベントエンティティを見出すとできること • registrationsにカラムを追加して、付加情報をもたせられる • 登録番号とか、登録時の申し送り ̀notè とか、登録種別(現地参加 xor オンライン参加)とか • 後続イベントの親レコードになれる • 例: 参加費用を決済したコト payments, 当日に参加したコト attendancesなど • ̀belongs̲to :registratioǹ なARモデルとなる • 登録したこと自体を記録できる • 監査ログに残したり、コンバージョン経路のログに残したりとか https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby?slide=22
  22. Model important occurrences as resources and work with them through

    CRUD. Railsの気持ちに沿うと、シンプルに実装できる • イベントエンティティを見出してRDBに表現し、has̲many :throughな ARモデルとして、一級に扱う • イベントエンティティに代表される「コト」をRESTリソースとして捉え やりたいことをそのCRUD操作で表現する • Railsが提供するインターフェースを思い描き、ARやAMo、 Rubyの柔軟性を活かして継続的に開発・リファクタリングしていく https://speakerdeck.com/moro/simplicity-on-rails-rdb-rest-and-ruby?slide=54
  23. As our understanding grows, we can change how we handle

    those events. 「そのとき」が来たらコードを変える • 「こう書くのがよさそう!」となったら試してみる。 • after̲commit から送ってみたり。 • レコード作成とメール送信を担うクラスを作ったり。 • Pub/Subな "イベント駆動" ぽいのを試してもいいかも。 • その時の書き味はどうですか? • いい感じなら続ける。しっくりこなかったら戻す。 • 変えたり戻したりするための仕組みを整えておく。 「システムの設計に毎日手を入れること。システムの設計は、その日のシステムの ニーズにうまく合致させること。」 (KentBeck 「エクストリームプログラミング」) / で、今日はここまで。その日が来たらそのぶんを設計します。 https://speakerdeck.com/moro/dynamic?slide=43
  24. ‣ Structured Event Reporting (SER) ‣ Event entities in relational

    database design ‣ Event-Driven Architecture (EDA)
  25. “ This happens when a system sends event messages to

    notify other systems of a change in its domain. A key element of event noti cation is that the source system doesn't really care much about the response. Martin Fowler, What do you mean by “Event-Driven”? fi https://martinfowler.com/articles/201701-event-driven.html
  26. Minimal EDA • The publisher does not need to know

    how subscribers are implemented • There may or may not be subscribers • Subscribers depend on the event, not on where it was published
  27. Minimal EDA in action • Persist the event entity, then

    wrap it in an event object and call notify • Subscribers use the event to perform subsequent processing, without depending on the publisher
  28. It is minimal • notify calls each subscriber's emit method

    sequentially • Slow processing in emit blocks notify • Exceptions in emit do not propagate to the caller by default in production; this is con gurable • Transactions are not handled automatically • All subscribers receive the same event object, fi so mutating it can cause problems
  29. It is built in • It takes some care to

    use, but having it built in matters • Loosely coupled patterns such as EDA are often deferred early in development • When the service grows and problems emerge, introducing them can be a big rst step fi • Start early, add subscribers as you learn
  30. Recap: Event-Driven Architecture • The caller reports that an event

    happened without knowing how subscribers handle it • Subscribers depend on the event, not on where it was published • Rails.event may be “the simplest thing that could possibly work”
  31. ‣ Structured Event Reporting (SER) ‣ Event entities in relational

    database design ‣ Event-Driven Architecture (EDA)
  32. Are we just playing word association with “event”? • Maybe

    so • But I see a common thread: Something important happened, • so we persist it in the database • so we log additional context • so we trigger subsequent processing
  33. Example: An imaginary online shop • A customer submits an

    order to OrdersController#create • We persist the order and its line items in the database • We report an event: an order was placed and successfully persisted
  34. Order.create! class OrderForm attr_reader :customer, :order, :items # ... def

    save! Order.transaction do |transaction| @order = customer.orders.create! items.each { order.order_items.create!(it) } end Rails.event.notify(Event::OrderPlaced.new(order:)) end end
  35. We want to send an order acknowledgement email メール送信コードはどこに置く? •

    場合による。「そのとき」まで考えるに留める。 • 「最もシンプルで、うまくいきそうなものは何ですか?」 • 考えるヒントは意識しておく。 • 今後どういうメールが増えそうか • Railsにどんな機能があるか • 自分たちのコードベースではどうなっているか Where should we locate email-sending code? → It depends.
  36. We want to send an email... why? • A customer

    placed an order̶something important happened, so we want to send an email • Think about it in reverse: • We want to send an email because an important event happened • Placing an order is a very important event
  37. An important event calls for more processing • An order

    matters, so we want to do more when it is placed • First, we need to reserve inventory • We also need to email the shop staff • We want to record the user's environment when the order was placed • IP address, Session ID, User-Agent, and so on • More needs will probably emerge
  38. I want to express this in clean Ruby code #

    Export events for analysis Rails.event.subscribe(LogExporter.new) # Enqueue jobs and send emails for matching events InventoryReservationJob.subscribe(Event::OrderPlaced) OrderMailer.subscribe(:placed, Event::OrderPlaced) OrderMailer.subscribe(:confirmed, Event::OrderConfirmed)
  39. Rails.event gives us a standard interface module Chirin::SubscribableJob # ...

    class JobSubscriber def initialize(job_class) @job_class = job_class end def emit(event) @job_class.perform_later(event.fetch(:payload)) end end module ClassMethods def subscribe(event_type, reporter: Rails.event) filter = ->(ev) { ev[:payload].is_a?(event_type) } reporter.subscribe(JobSubscriber.new(self), &filter) end end end
  40. Next: Where should we publish events? • Now we can

    receive events. Where should we publish them? • In a controller or form object? • In an Active Record callback?
  41. The same Order.create can represent different events • Suppose we

    import in-person sales as orders too • We do not send an email for each order • We do not need to reserve inventory, take online payments, or ship the goods • We may need different processing, such as emailing the staff when the import is complete • Creating an Order does not always mean an order was just placed
  42. Discovering Event::InPersonOrdersImported • Different follow-up processing from Event::OrderPlaced • No

    need to send an acknowledgement email for each order • We may notify staff when the import is complete • We may invite in-person customers to register online • We discover that placing an online order and importing in-person sales are different events
  43. An event represents something important in our service • The

    event is what matters • An important event happened, so we need subsequent processing • The need for subsequent processing can help us identify an important event • Different events can create records in the same table; the records in same table can represent different events
  44. Recap: Understand the service through its events • As we

    build, we discover important occurrences in our service and express them in code • With the standard Rails.event interface, we can act on those discoveries right away • We are architecting, driven by events ̶ a little wordplay
  45. Thank Rails • Rails.event prompted me to think about what

    events mean in an application • It deepened my understanding of my work as a programmer • My work is to identify important events in the service and capture that understanding in code
  46. Sharing my "Oh, that's what it is!" moment • They

    are all events! • An event represents something important that happened in the service • "Oh, that's what it is!”
  47. “ Ultimately, something is simple for me if I feel

    good about it. Does it have that elusive quality without a name? Can I walk away from the keyboard without nagging doubts? Does it feel right? 結局のところ、私にとって良い感じがするものならば、それは私にとってシンプルだ。 そこに、 葉で い表せない無名の質があるだろうか?疑念に悩まされることなく キーボードから離れられるだろうか? しっくりときているだろうか? Dave Thomas “simplicity” 言 言 (訳は島田浩二 “シンプリシティ”より)
  48. It feels right to me • For me, Rails provides

    an abstraction layer that helps me deepen my understanding of the service • This discovery about events is an example • Deepening that understanding can contribute to theory building
  49. Deepening our understanding in the AI era アプリケーション開発者はAI時代どうあったら良いか • 正解はわからない…

    • けれど、自分たちのシステムに必要なアーキテクチャを捉え、判断して いけるには、自分の中に「理論」は必要 • 理論 = 問題領域と解決領域を「よくわかっている」こと • 理論は開発を通じて構築されていくもの。だから… • 開発を重ねながら、チームに、人に、そしてAIにすら、理論が育つよう な方法を模索していきましょう https://speakerdeck.com/snoozer05/see-through-the-master
  50. From events to understanding and theory building • Structured Event

    Reporting in Rails got me thinking about “events” • An event can represent something important that happened in our service • For me, understanding our service through its events feels right