Love Ruby, Rails and XP. • Past Kaigi on Rails Talks available on YouTube & SpeakerDeck @moro ( • dynamic! • Identifying User Identity • Simplicity on Rails )
about “events” • An event can represent something important that happened in our service • For me, understanding our service through its events feels right
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
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
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
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
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
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
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
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
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
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”
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
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
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
場合による。「そのとき」まで考えるに留める。 • 「最もシンプルで、うまくいきそうなものは何ですか?」 • 考えるヒントは意識しておく。 • 今後どういうメールが増えそうか • Railsにどんな機能があるか • 自分たちのコードベースではどうなっているか Where should we locate email-sending code? → It depends.
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
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
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)
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
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
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
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
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
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
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” 言 言 (訳は島田浩二 “シンプリシティ”より)
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
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