Slide 1

Slide 1 text

Event is what happens Kaigi on Rails 2026 2026-10-16 諸橋恭介 @moro

Slide 2

Slide 2 text

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 )

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Event is what happens

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Events: • Structured Event Reporting (SER) • Event entities in relational database design • Event-Driven Architecture (EDA)

Slide 7

Slide 7 text

Rails 8.1 comes with "Structured Event Reporting" https://www.youtube.com/watch?v=JK90_2L_NUM https://github.com/rails/rails/pull/55334

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Event is what happens when something important comes true.

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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 })

Slide 16

Slide 16 text

Subscriber#emit class JSONEventSubscriber def emit(event) json_data = JSON.generate(event) LogExporter.export(json_data) end end Rails.event.subscribe(JSONEventSubscriber.new)

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

“ 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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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”

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Event is what happens when something important comes true.

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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)

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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?

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

outro

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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!”

Slide 55

Slide 55 text

“ 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” 言 言 (訳は島田浩二 “シンプリシティ”より)

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Deepening our understanding in the AI era アプリケーション開発者はAI時代どうあったら良いか • 正解はわからない… • けれど、自分たちのシステムに必要なアーキテクチャを捉え、判断して いけるには、自分の中に「理論」は必要 • 理論 = 問題領域と解決領域を「よくわかっている」こと • 理論は開発を通じて構築されていくもの。だから… • 開発を重ねながら、チームに、人に、そしてAIにすら、理論が育つよう な方法を模索していきましょう https://speakerdeck.com/snoozer05/see-through-the-master

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Event is what happens when something important comes true.