We’ve been looking at how funds-transfer
component leverages account-component
to make transfers. As part of that flow, funds-transfer-component
handles Withdrawn
and Deposited
events from account-component
.
The basic flow of handlers
You can break a handler into four steps:
- Figure out which entity you’re working on
- Project the current state of that entity
- Check to see if you’ve already processed the current message
- If you haven’t, process the message and record that you’ve done so by writing an event
Figuring out the entity when it isn’t your event
funds-transfer-component
has a challenge. When you’re handling an event from another component that isn’t aware of your workflow, how do you know which entity you’re working on? When funds-transfer-component
handles its own Initiated
event, you have something like:
handle Initiated do |initiated|
funds_transfer_id = initiated.funds_transfer_id
funds_transfer, version = store.fetch(
funds_transfer_id,
include: :version
)
# ...
end
The problem? account-component
’s events don’t contain a funds_transfer_id
. If they did, you’d have to go add new data properties every time you wanted to do something new with account-component
. Which means you’d have to wait for account-component
’s team to be available to make those changes. Goodbye, Flow.
Are we just out of luck? Yes, time to go play Minecraft.
Stream names and correlation_stream_name
j/k. No, do you remember the correlation_stream_name
property? What did funds-transfer-component
use as the value there? Something of the form fundsTransfer-123
. We chose that on purpose.
Do you remember the pieces of a stream name? Everything to the left of the first dash is the category and everything to the right of the first dash is the… 🥁… identifier The identifier of what? The identifier of the entity that you’re working on.
Which means that your correlation_stream_name
value is of the form fundsTransfer-<funds_transfer_id>
.
So, splitting that stream name at the first dash and taking the second part gives you your fundsTransferId
:
handle ::Account::Client::Messages::Events::Withdrawn do |account_withdrawn|
correlation_stream_name =
account_withdrawn.metadata.correlation_stream_name
funds_transfer_id = Messaging::StreamName.get_id(
correlation_stream_name
)
funds_transfer, version = store.fetch(
funds_transfer_id,
include: :version
)
# ...
end
Consuming only the events you care about
The last step is to make sure that funds-transfer-component
only handles account-component
messages it cares about. The other day we mentioned the [get_category_messages
function in Message DB](https://github.com/message-db/message-db/blob/master/database/functions/get-category-messages.sql).
How to leverage that in your system will depend on the technology you use, but in Eventide you leverage it when you start your consumber:
Consumers::Account::Events.start(
'account',
correlation: 'fundsTransfer'
)
Notice the correlation
option we pass. The consumer will only retrieve messages whose metadata.correlation_stream_name
is in the fundsTransfer
category.