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

Event is what happens

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

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