You can achieve great Flow in your software development organization when your codebase doesn’t fight you. Autonomous components will help you get and maintain such a codebase.
Don’t mistake the autonomy of a component for thinking that it never collaborates with one another. We just mean it has a definite scope over a distinct process and only the data it needs to carry out that process.
account-component
owns the distinct process of tracking a balance. You can imagine several reasons why a balance might change. Purchases, fees, transfers, etc. These other reasons cause account-component
to publish events. funds-transfer
component needs to observe the events it caused but ignore the ones it didn’t.
Getting meta
Messages in Message DB have a plain JSON object metadata
property, similar to their data
property. While you can put whatever you want in there, The Eventide Project uses metadata
strictly for properties related to the mechanics of messaging. So if a piece of data is something used in authorization, your domain model, or might show up on a screen, don’t put it in metadata
.
In our cross-component case, funds-transfer-component
assigns a correlation_stream_name
property to the commands it sends to account-component
. For example:
handle Initiated do |initiated|
funds_transfer_id = initiated.funds_transfer_id
# ...
withdraw = Account::Client::Messages::Commands.follow(initiated)
withdraw.metadata.correlation_stream_name = stream_name(funds_transfer_id)
# ...
write.(withdraw, account_command_stream_name)
end
It uses the stream name of the current transfer as the value. So, if we’re processing the transfer living in fundsTransfer-123
, then fundsTransfer-123
goes into the correlation_stream_name
.
Every component has the understanding that when it handles a message with a metadata.correlation_stream_name
, it copies that onto any resulting events. So when account-component
writes the Withdrawn
triggered by fund-transfer-component
’s Withdraw
command, that event will also have the same metadata.correlation_stream_name
.
When funds-transfer-component
consumes the account
category, it can ignore any event that doesn’t have a metadata.correlation_stream_name
in the fundsTransfer
category. Message DB provides a mechanism for doing so in its get_category_messages
function.
account-component
didn’t have to change to support transfers
When funds-transfer-component
handles those account
messages, it has some special considerations that we’ll cover next time we get low level like in this email (I’ve learned over the past week to not say “tomorrow”).
But this publication seeks to increase Flow in your development organization. Did you see how account-component
didn’t have to change to support transfers? It has absolutely no conception about what transfers are. That’s a marker of an autonomous component.
Which means that if you had account-component
in your system, you could add new functionality without going back and changing anything. If separate teams wrote the two components, they wouldn’t have needed to wait on each other. When you want to add overdraft detection or fees to your system, you can add them without having to revamp account-component
.
This works because account-component
is generic. It doesn’t care one wit about why a balance is changing. That responsibility lies with funds-transfer-component
or a purchasing component or an overdraft fee component.
With an architecture like that your codebase won’t prevent Flow anymore.