chemist
CQRS/ES commanded questions
Hi, I am working with the commanded library and will love to hear some of your views.
-
What is the difference between a saga and a process manager? Because I sense the process manager in commanded should be named a saga as it does route messages across multiple discrete contexts/aggregates.
-
How should I write compensating actions using commanded proc mgr?
For example, let’s say I create a wallet where monies are to be drawn out of a customer’s account. This create_wallet command is encapsulated in a proc mgr which will then trigger a corresponding withdraw_account command. What if for some reason the account table is offline? Or for some reason, the transaction falls through like if a new withdrawal policy is enacted system-wide midway?
- Just wondering whats the advantage of the proc mgr in commanded apart from compensating actions and workflows as aggregates can emit multiple events from a single command? If i understood correctly, I dont see much difference between the two below:
A saga that listens for a WalletCreated event to trigger a WithdrawAccount command which emits a AccountWithdrawn event:
defmodule MyProj.Sagas.CreateWallet
def interested?(%WalletCreated{transfer_uuid: transfer_uuid})
when not is_nil(transfer_uuid) do
{:start!, transfer_uuid}
end
def interested?(%AccountWithdrawn{transfer_uuid: transfer_uuid}) when not is_nil(transfer_uuid) do
{:continue!, transfer_uuid}
end
def handle(%CreateWallet{}, %WalletCreated{transfer_uuid:
transfer_uuid, wallet_id: wallet_id, funds_balance: funds_balance,
currency: currency}) do
%WithdrawAccount{
account_id: account_id,
transfer_uuid: transfer_uuid,
withdrawal: Money.to_decimal(funds_balance),
currency: Cldr.validate_currency(currency) |> elem(1)
}
end
def apply(%CreateWallet{} = saga, %WalletCreated{} = event) do
%CreateWallet{
saga
| transfer_uuid: event.transfer_uuid,
account_id: event.account_id,
destination_wallet_id: event.wallet_id,
funds: event.funds_balance,
currency: event.currency,
status: :withdraw_from_account
}
end
def apply(%CreateWallet{} = saga, %WalletWithdrawn{}) do
%CreateWallet
{
saga
| status: :deposit_into_wallet
}
end
vs a single aggregate emitting two events WalletCreated and AccountWithdrawn which will also be projected and produce the same results as above
def execute(%Wallet{wallet_id: nil}, %CreateWallet{wallet_id:
wallet_id, account_id: account_id, funds_balance: funds_balance,
currency: currency, transfer_uuid: transfer_uuid} = _create) do
with validated_funds_balance <- {Money.new(currency, funds_balance)}
do
IO.inspect(transfer_uuid)
%WalletCreated
{
wallet_id: wallet_id,
account_id: account_id,
funds_balance: validated_funds_balance,
currency: currency,
transfer_uuid: transfer_uuid
}
%AccountWithdrawn
{
account_id: account_id,
transfer_uuid: transfer_uuid,
withdrawal: Money.to_decimal(funds_balance),
currency: Cldr.validate_currency(currency) |> elem(1)
}
else
_ -> {:error, "The wallet cannot be created at this time.}
end
end
Many thanks.
Marked As Solved
slashdotdash
What is the difference between a saga and a process manager?
A process manager is responsible for coordinating multiple aggregates. You can use a process manager to implement the Saga pattern. This is a way of implementing long-lived transactions (spanning minutes, hours, or days) that are written as a sequence of transactions that can be interleaved. All transactions in the sequence complete successfully, or compensating transactions are run to amend a partial execution (rollback on error). The error/3 callback function in a Commanded process manager is where you can handle errors and can execute any rollback commands as applicable.
Distributed Sagas: A Protocol for Coordinating Microservices by Caitie McCaffrey is a really good talk introducing the saga pattern.
In your example since both events are produced by a single aggregate there is no need for a process manager. Emitting two events from the aggregate would be the preferred approach.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









