From 68ae49df8294e01b1eccefa66fd4c9a6e3f1aa1f Mon Sep 17 00:00:00 2001 From: Pawel Kondzior Date: Tue, 29 May 2018 17:52:49 +0200 Subject: [PATCH 1/4] Update docs to use new mapper API --- .../source/docs/protobuf.html.md.erb | 10 ++++++++-- .../source/docs/subscribe.html.md | 20 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/railseventstore.org/source/docs/protobuf.html.md.erb b/railseventstore.org/source/docs/protobuf.html.md.erb index 5b7a439641..548524b7e5 100644 --- a/railseventstore.org/source/docs/protobuf.html.md.erb +++ b/railseventstore.org/source/docs/protobuf.html.md.erb @@ -108,10 +108,16 @@ event_store.subscribe(->(ev){ }, to: [MyApp::OrderPlaced.descriptor.name]) class SendOrderEmailHandler < ActiveJob::Base self.queue_adapter = :inline - def perform(event) - event = YAML.load(event) + def perform(payload) + event = event_store.deserialize(payload) # do something end + + private + + def event_store + Rails.configuration.event_store + end end event_store.subscribe(SendOrderEmailHandler, to: [MyApp::OrderPlaced.descriptor.name]) diff --git a/railseventstore.org/source/docs/subscribe.html.md b/railseventstore.org/source/docs/subscribe.html.md index 5fbf061734..d1d06b1a02 100644 --- a/railseventstore.org/source/docs/subscribe.html.md +++ b/railseventstore.org/source/docs/subscribe.html.md @@ -264,12 +264,25 @@ You start the temporary subscription by providing a block `within` which the sub

Async handlers

-It's possible to also subscribe async handlers to events. Async handlers are just background jobs implemented with `ActiveJob`. +It's possible to also subscribe async handlers to events. Async handlers are just background jobs implemented with `ActiveJob`. You have to remember that events are serialized during asynchronous processing, You will have to use `Client#deserialize` method in order to get domain event from ActiveJob payload: ```ruby +module AsyncHandler + def perform(payload) + super(event_store.deserialize(payload)) + end + + private + + def event_store + Rails.configuration.event_store + end +end + class SendOrderEmail < ActiveJob::Base + prepend AsyncHandler + def perform(event) - event = YAML.load(event) email = event.data.fetch(:customer_email) OrderMailer.notify_customer(email).deliver_now! end @@ -306,8 +319,9 @@ You can configure your dispatcher slightly different, to schedule async handlers ```ruby class SendOrderEmail < ActiveJob::Base + prepend AsyncHandler + def perform(event) - event = YAML.load(event) email = event.data.fetch(:customer_email) OrderMailer.notify_customer(email).deliver_now! end From af1f59fb9c93195a4574b666b289d3d60c8d2ba5 Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Tue, 26 Jun 2018 22:13:50 +0200 Subject: [PATCH 2/4] Document how to correlate events in Async Handlers Issue: #346 [ci skip] --- .../source/docs/correlation_causation.html.md | 30 +++++++++++++++++-- .../source/docs/subscribe.html.md | 19 ++++++++---- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/railseventstore.org/source/docs/correlation_causation.html.md b/railseventstore.org/source/docs/correlation_causation.html.md index 4485890295..144acf4e49 100644 --- a/railseventstore.org/source/docs/correlation_causation.html.md +++ b/railseventstore.org/source/docs/correlation_causation.html.md @@ -53,13 +53,37 @@ new_event.metadata[:correlation_id] new_event.metadata[:causation_id] ``` +This is however not necessary for sync handlers. Events published from sync handlers are by default correlated with events that caused them. + +## Correlating events published from async handlers + +Events published from async handlers are not correlated with events that caused them by default. To enable that functionality you need to prepend `RailsEventStore::CorrelatedHandler` + +```ruby +class SendOrderEmail < ActiveJob::Base + prepend RailsEventStore::CorrelatedHandler + prepend RailsEventStore::AsyncHandler + + def perform(event) + event_store.publish(HappenedLater.new(data:{ + user_id: event.data.fetch(:user_id), + })) + end + + private + + def event_store + Rails.configuration.event_store + end +end +``` + ## Correlating an event with a command -If your command responds to `correlation_id` (can even always be `nil`) and `message_id` you can correlate your events -also with commands. +If your command responds to `correlation_id` (can even always be `nil`) and `message_id` you can correlate your events also with commands. ```ruby -class ApproveOrder = < Struct.new(:order_id, :message_id, :correlation_id) +class ApproveOrder < Struct.new(:order_id, :message_id, :correlation_id) end command = ApproveOrder.new("KTXBN123", SecureRandom.uuid, nil) diff --git a/railseventstore.org/source/docs/subscribe.html.md b/railseventstore.org/source/docs/subscribe.html.md index d1d06b1a02..0fd836ec89 100644 --- a/railseventstore.org/source/docs/subscribe.html.md +++ b/railseventstore.org/source/docs/subscribe.html.md @@ -264,12 +264,14 @@ You start the temporary subscription by providing a block `within` which the sub

Async handlers

-It's possible to also subscribe async handlers to events. Async handlers are just background jobs implemented with `ActiveJob`. You have to remember that events are serialized during asynchronous processing, You will have to use `Client#deserialize` method in order to get domain event from ActiveJob payload: +It's possible to also subscribe async handlers to events. Async handlers are just background jobs implemented with `ActiveJob`. ```ruby -module AsyncHandler +class SendOrderEmail < ActiveJob::Base def perform(payload) - super(event_store.deserialize(payload)) + event = event_store.deserialize(payload) + email = event.data.fetch(:customer_email) + OrderMailer.notify_customer(email).deliver_now! end private @@ -279,8 +281,15 @@ module AsyncHandler end end +event_store = RailsEventStore::Client.new +event_store.subscribe(SendOrderEmail, to: [OrderPlaced]) +``` + +You can also use `RailsEventStore::AsyncHandler` module that will deserialize the event for you: + +```ruby class SendOrderEmail < ActiveJob::Base - prepend AsyncHandler + prepend RailsEventStore::AsyncHandler def perform(event) email = event.data.fetch(:customer_email) @@ -319,7 +328,7 @@ You can configure your dispatcher slightly different, to schedule async handlers ```ruby class SendOrderEmail < ActiveJob::Base - prepend AsyncHandler + prepend RailsEventStore::AsyncHandler def perform(event) email = event.data.fetch(:customer_email) From 744982dde59c4b4449b180609214081ee7c717a6 Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Fri, 29 Jun 2018 16:53:21 +0200 Subject: [PATCH 3/4] Rename --- .../source/docs/{subscribe.html.md => subscribe.html.md.erb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename railseventstore.org/source/docs/{subscribe.html.md => subscribe.html.md.erb} (100%) diff --git a/railseventstore.org/source/docs/subscribe.html.md b/railseventstore.org/source/docs/subscribe.html.md.erb similarity index 100% rename from railseventstore.org/source/docs/subscribe.html.md rename to railseventstore.org/source/docs/subscribe.html.md.erb From e9b164ff46f5ef42c65851b39717d3b66c166da5 Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Fri, 29 Jun 2018 17:06:18 +0200 Subject: [PATCH 4/4] Document linking to streams by metadata Issue: #346 #221 [ci skip] --- .../source/docs/correlation_causation.html.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/railseventstore.org/source/docs/correlation_causation.html.md b/railseventstore.org/source/docs/correlation_causation.html.md index 7b3ab2837c..ddce8d5bc0 100644 --- a/railseventstore.org/source/docs/correlation_causation.html.md +++ b/railseventstore.org/source/docs/correlation_causation.html.md @@ -151,6 +151,39 @@ class AddProductCommand < Struct.new(:message_id, :product_id) end ``` +## Building streams based on correlation id and causation id + +You can use `RailsEventStore::LinkByCorrelationId` (`RubyEventStore::LinkByCorrelationId`) and `RailsEventStore::LinkByCausationId` (`RubyEventStore::LinkByCausationId`) to build streams of all events with certain correlation or causation id. This makes debugging and making sense of a large process easier to see. + +```ruby +Rails.application.configure do + config.to_prepare do + Rails.configuration.event_store = event_store = RailsEventStore::Client.new + event_store.subscribe_to_all_events(RailsEventStore::LinkByCorrelationId.new) + event_store.subscribe_to_all_events(RailsEventStore::LinkByCausationId.new) + end +end +``` + +After publishing an event: + +```ruby +event = OrderPlaced.new +event_store.publish(event) +``` + +you can read events caused by it: + +```ruby +event_store.read.stream("$by_causation_id_#{event.event_id}") +``` + +and events correlated with it: + +```ruby +event_store.read.stream("$by_correlation_id_#{event.correlation_id || event.event_id}") +``` + ## Thanks Image thanks to [Arkency blog](https://blog.arkency.com/correlation-id-and-causation-id-in-evented-systems/) \ No newline at end of file