1
votes

so currently I'm implementing a saga with axon framwork, using event sourcing and CQRS.

Circumstances are the following:

I have 3 Microservices, m1, m2 and m3

A user enters data for 3 entities e1, e2 ,e3 in a GUI that are handled and persisted by m1,m2,m3 respectively, so m1->e1,m2->e2,m3->e3

Now to the necessity of the saga:

e1 can't exist without e2 and e2 cant exist without e3.

So all 3 entities will have to be successfully created by their respective services and if one fails, the saga needs to perform compensating transactions to ensure consistency. First m1 creates e1, emits an e1CreatedEvent and an orchestrator sends a createE2Command as a reaction to that etc.

Now my problems:

How do I get/store the information that the user enters? In a single RequestBody? Then what do I do with that data? Because it needs to be cached somehow in order to be sent with the commands.

For example m1 creates a createE1Command and adds the information from e1 to it, then after it is successfully created the orchestrator will initiate a createE2Command and THEN add the info for e2 to that command before sending it....for that to happen, the info for e2 needs to be stored somehow until it is needed.

Sample Code:

@Saga
public class ManagementSaga{

@Autowired
private transient CommandGateway commandGateway

@StartSaga
@SagaEventhandler
public void handle (e1CreatedEvent e1CreatedEvent){


  commandGateway.send (new CreateE2Command (e1CreatedEvent.Id, **HERE NEEDS TO BE THE INFO THAT THE USER CREATED PREVIOUSLY**)}}

Do I just create an object that holds the information for these three entities? That feels really wrong.

Now I understand that maybe it is really bad to choose such a domain model and one should probably avoid it if possible, but it's for sceience :-)

1

1 Answers

2
votes

My first reaction in such scenarios is to questions the design altogether. It seems that the (micro)services are design around entities. This type of design may work in some scenarios, but will inevitably lead to a distributed monolith in others. The challenges you are facing hint towards the latter.

There also seems to be a mismatch in the interaction from the API perspective and the way the system is designed. While not per definition wrong, this may also be an indication that either the API is incorrect (doesn't properly reflect the behavior of the system), or that the model of the behavior is incorrect.

To give you an answer anyway that is hopefully more useful than "it depends", you can have a single interaction result in multiple commands or even queries. It doesn't require a Saga in all cases.

Take a single request with information about E1, E2 and E3. Then in your controller, send commands to create E3, based on the results of that command, create E2, and finally create E1. No information needs to be "cached". The request information is available throughout the entire process. This is approach is, however, not transactional. If the service crashes, it may leave the process half-executed.

An alternative is to have a command handler that registers the original intent of the user. That is not to create E1, or E2 or E3, but something that encompasses all three. The event that process generates, triggers a Saga to execute each step individually, using events from those steps to trigger the next step. This process is transactional with Eventual Consistency characteristics.

I hope this made some sense. However, I would seriously reconsider the model/design of the services, to have them focus more on the process (steps) and less on entities (state). I've seen too many Microservices projects fail because of entity-centric design...