Rethinking the roles of commands and reactions in the Decide, Evolve, React pattern. Decide really is just about invariants and nothing else. React is the real brains of the system.
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan I understand that when decisions are binary (either do something or don't do it because of an error X). But as soon as the decider starts to potentially return multiple events, I think perspective changes (at least mine).
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt can you elaborate? I wanted to record my current thinking but I don’t think I’ve reached clarity yet.
=> More informations about this toot | More toots from ismasan@ruby.social
@katafrakt but yeah you’re right. Deciders can also express behaviour via events that goes beyond guarding invariants… I guess depending on how you think about invariants 🤔
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan that's more or less what I meant. When you are just guarding invariants, your decision is only a success/failure. However it might be different in some domains.
For example, when you have Loans, in response to "register payment" you might decide just "payment registered" but also additionally "loan paid back" if it's the last installment. This is beyond invariants and IMO is the heart of a domain.
=> More informations about this toot | More toots from katafrakt@ruby.social
@ismasan another example could be the meetup clone example I shared. When the user "cancels RSVP" you can just remove them from the attendance list, but maybe also bring in someone from the waitlist or mark the event as not full anymore.
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt yes that’s an interesting example because there’s an argument that the command handler shouldn’t do that extra work, and that a processor should instead look at the state of things after the fact and decide to dispatch a new command to signup someone from the wait list.
=> More informations about this toot | More toots from ismasan@ruby.social
@katafrakt I’m not necessarily sold on this btw, just reflecting how it’s argued elsewhere. For example in Event Modeling command handlers that emit more than one event are not forbidden; but sort of seen as a design red flag.
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan I see. In that case I think one is indeed reducing decider to be a guardian of the invariants (and a boring thing).
Returning multiple events was the main thing that drew me to the decider pattern. I think it's quite clear in Jeremie's blog post that it's "legal". I won't easily give that up, even if I would be doing something "impure" ;)
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt so do you run side effects in Decide, then? I think in Jeremie ‘s original post Decide functions are supposed to be pure, too (but I could be wrong)
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan definitely no side effects in the decider for me. I introduced a side effects runner and called it reactor, which takes a list of events and reacts to them one by one. But I should probably change the name, because it triggers some people - as a reactor in f model, for example, is something different.
I'm mostly only after separating logical decision from infrastructural execution. I think I should write a blog post about that motivation.
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt how do you track those side effects though. Presumably the reactor (I call them reactors too) runs the side-effect and then dispatches a new command with the results? And if the side effect can result in different scenarios, then the reactor can decide what command to send next. I think that level of decision-making in the reactor is what collided (in my mind) with the decision-making in Deciders.
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan do I need to track them? So far it's making a DB insert/update or scheduling a background job. If I run it in a transaction it either all fails or all succeeds. The reactor does not send any commands itself.
I get this might be naive, but so far in my experimentation I did not need anything more.
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt but for example if the side effect is to make a payment using Stripe’s API. If the payment succeeds, you continue with the happy path of the workflow. If it fails (say because of lack of funds), you do something else.
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan this would happen inside a background job (scheduled by a reactor). And the job calls the thingy that coordinates decider with reactor (Phoenix context in my case, but might be any other orchestrating layer), just as a web request from the user would do.
=> More informations about this toot | More toots from katafrakt@ruby.social
@katafrakt right ok but conceptually it’s the reactor scheduling the side effect and the next command. The bg job is just the scheduling mechanism
=> More informations about this toot | More toots from ismasan@ruby.social
@ismasan I would say it's Stripe gateway issuing a command, just like the user is issuing a command by filling out a form and hitting "submit". However I am starting to see some gaps in this concept. I think I will have to re-examine it.
I guess I'm reluctant to reactor producing commands because they will be very infra-oriented, like "send email", "update database record". These are very different from domain-oriented commands.
=> More informations about this toot | More toots from katafrakt@ruby.social
@ismasan @katafrakt yes decide is pure (there can be some exception though)
=> More informations about this toot | More toots from thinkb4coding@mastodon.social This content has been proxied by September (3851b).Proxy Information
text/gemini