Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CQRS is saga/process manager the best approach for executing a set of related commands synchronously?

I have a CQRS .NET Core service that currently has a single endpoint for each command. Sometimes a collection of commands makes up a single action in the system (e.g. create an assessment) and 3 or so commands have to be executed synchronously once the previous command has commited data to the db (this is because of the legacy database design).

At the moment this is handled by the client where each command fires after the it's previous dependent one has completed (using promises).

It's worth noting that these commands are also executed individually at times and if they were combined in to a single command it would end up being massive and very hard to test.

What I want to do is create a single endpoint that manages operations like our create assessment one, which has to execute the following commands one after the other:

  1. UserCreated
  2. AssessmentCreated
  3. AssessmentRegisteredToClient

I've have been going down the route of using a MassTransit saga/state machine/process manager but the more I work on it (and the more my understanding improves) the more I think it seems like overkill and not the right approach (as these are commands and not events and all within a single bounded context).

Does a process manager seem like the right fit and if so is the MassTransit state machine implementation the way to go or should I look at other sources? Or should I just simply create an endpoint that fires each command one at a time (i.e. have this synchronous code in the ASP controller)?

like image 430
Ben Thomson Avatar asked Jan 03 '23 15:01

Ben Thomson


1 Answers

Just to clarify, UserCreated is not a command, it is an event.

You have three choices here:

  • Build a reactive system instead. So, after CreateUser (btw, not a very good language here since you never create your users, they register in your system) you emit UserCreated, which causes a reaction that sends a command CreateAssessment and so on.
  • Use MT saga as a process manager. State machines are very well suited if you need to ensure that your somewhat complex workflow completes (or fails) in a way you expect.
  • Use MT courier feature to implement a distributed transaction. You will have a sequence of activities, actions and compensating actions (reversals).

I would personally analyse my problem to find out what is the bare minimum I need to do before I can return to the user and say "it's ok". The rest can be done asynchronously, behind the scenes. It is more to the domain analysis than for the tech.

Update 04/12/2019, answering one of the comments:

Again, there's an issue with the terminology that is currently applied. Many messaging libraries, like MassTransit, Rebus or NServiceBus use the word saga for process managers.

Using the original terminology, process managers handle the whole process with possible deviations and parallelisations. Sagas, on the other hand, implement a single-flow process. When any step in the saga fails, everything that happened before gets reverted using compensating actions. There's no orchestration for sagas as each step is independent and when one step is complete, the next step gets executed. Sagas are always stateless and if there's a need to include some additional information for the next step, the saga activity needs to put it to the payload.

A process manager is, by definition, the orchestrator. It never does the work themselves, but instruct other components to do the job. Then, by listening to events, the process manager decides how the process should flow. The process manager can initiate a compensation action, but it doesn't perform the action itself, it instruct another component to do the job. For the purpose of keeping the current state of the process, process managers are stateful.

like image 120
Alexey Zimarev Avatar answered Apr 29 '23 05:04

Alexey Zimarev