Managing complex business transactions

Hi Allard and all.

I’m working with Axon for our production system (not a POC anymore) and even if sometimes I see clearly what to do more often than not I’m still puzzled and baffled. A simple implementation of a simple aggregate looks clear, but when a aggregate has to interact with other parts of the system things get complicated.

My Task aggregate currently has something like this:

`
@CommandHandler
public void handle(StartTaskCommand command) {
boolean ok = userService.checkUserPermissions(command.getOwner(), this.task);
if(ok){
apply(new TaskStartedEvent(…));
}else{
apply(new TaskStartedFailedEvent(…));
}
}

@EventSourcingHandler
public void on(TaskStartedEvent event) throws Exception {
task.setState(TaskState.IN_PROGRESS);
}

@EventSourcingHandler
public void on(TaskStartedFailedEvent event) throws Exception {
task.setState(TaskState.FAILED);
}
`

1 command, 2 events…

So far so good. But if that checkUserPermissions is actually some more complicated thing that interacts to the world, what do I do? Using a Saga I will have to do something like:

`
@CommandHandler
public void handle(StartTaskCommand command) {
apply(new TaskStartingEvent(…));
}

@EventSourcingHandler
public void on(TaskStartingEvent event) throws Exception {
task.setState(TaskState.STARTING);
}

@EventSourcingHandler
public void on(TaskStartedEvent event) throws Exception {
task.setState(TaskState.IN_PROGRESS);
}

@EventSourcingHandler
public void on(TaskStartedFailedEvent event) throws Exception {
task.setState(TaskState.FAILED);
}

`

1 command, 3 events…

Plus the saga:

`
@SagaEventHandler(associationProperty = “taskId”)
public void handle(TaskStartingEvent event) {
ComplicatedCommand command = new ComplicatedCommand(event.getId());
commandGateway.send(command);
}

@SagaEventHandler(associationProperty = “taskId”)
public void handle(ComplicatedThingFailed event) {
TaskFailStartCommand command = new TaskFailStartCommand(event.getId(), event.getReason());
commandGateway.send(command);
}

@SagaEventHandler(associationProperty = “taskId”)
public void handle(ComplicatedThingSucceeded event) {
TaskContinueStartCommand command = new TaskContinueStartCommand(event.getId(), event.getReason());
commandGateway.send(command);
}
`

So now in my Aggregate I have to add 2 more commands
TaskFailStartCommand

TaskContinueStartCommand (???)

giving 3 commands + 3 events to do only one thing… Considering the HumanTask specification has more than 50 commands of which I pretend to use at least 16, using this structure I will have 16 commands * 3 + 16 events * 3, total 96 on the Aggregate only!!! On the Saga I will have 1 command + 2 events + 1 timeout for each call I want to make to the “outside world”… or use sync calls instead, but that defeats the purpose of Sagas.

Does this sounds right to you? I know this is more a rant than a question, but I’m a bit lost here…

Thanks.

Hi Antonio,

it seems that the most pragmatic approach is to simply query for some information to validate your command. In the end, because you’re using CQRS, that query should be sufficiently fast anyway.
Note that you can put extra parameters to your @CommandHandler method. Axon will automatically inject spring beans if it can resolve one.

Using Sagas will allow everything to happen in a non-blocking fashion, but it does bring a lot of complexity in the mix.

Cheers,

Allard

Forgot the other option:
you can also use an interceptor to perform authorization. Any information you need to authorize the command can be taken from either the command itself, the execution context or by performing a query.