Axon @QueryHandler with Spring @ExceptionHandler
Asked Answered
A

1

6

Having troubles with using Exception thrown in Axon @QueryHandler that is annotated as Spring @ResponseStatus. The original Exception is swallowed by QueryHandler and Axon specific AxonServerRemoteQueryHandlingException is thrown what actually gives 500 when spring responds to client

It is still possible to harvest some info from the Axon exception, like the original "Entity not found" message but not Exception type nor any other info the original exception holds.

Q1: is there is any way how to promote the exception thrown in the Query handler into Spring response as 404

Spring Exception handler

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends ServiceException() {
  ...
}

Axon query handler

@QueryHandler
public Application getApplicationById(ApplicationByIdQuery query) {
  return applicationRepository.findById(query.getId())
      .orElseThrow(() -> new NotFoundException(Application.class, query.getId()));
}

Spring controller

@Autowired
QueryGateway queryGateway;

@GetMapping(path = "/{applicationId}")
public CompletableFuture<Application> getApplication(@PathVariable String applicationId) {
  return queryGateway.query(new ApplicationByIdQuery(applicationId), ResponseTypes.instanceOf(Application.class));
}

Actual result json:

{
  "timestamp": "2019-02-08T08:04:03.629+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "An exception was thrown by the remote message handling component.",
  "path": "/api/applications/dff59c46-baf1-40f5-8a21-9286d1f8e36fx"
}

Q2: My another question is why not to use regular JPA Query API directly but use the QueryHandler from Axon. The projection tables are regular JPA tables and could be questioned via very powerful Spring JPA. Is it because direct querying does not ensure projection data consistency? I went through many examples, most of them use the direct access (see bellow), the rest do not solve exceptions thrown from underlaying QueryHandler

@Autowired
ApplicationRepository applicationRepository;

public CompletableFuture<Application> getApplication(@PathVariable String applicationId) {
  return CompletableFuture.supplyAsync(() -> applicationRepository.findById(applicationId)
                .orElseThrow(() -> new NotFoundException(Application.class, applicationId)));   
}
Anchorite answered 8/2, 2019 at 8:19 Comment(0)
E
9

I hope I can help you with some suggestions in this space.

Answer to question 1:

Axon Server will always wrap a command, event or query dispatching/handling exception into something else.

Thus if you'd want to conditionally react on a remote query handling exception, I think you'll have to add an @ExceptionHandler(AxonServerRemoteQueryHandlingException.class) annotated function in your code. The AxonServerRemoteQueryHandlingException contains more information about the exception it's wrapping, thus providing your a handle to send specific response if that's necessary.

The format of the wrapped exception is however not ideal at the moment. From Axon Framework/Server 4.1 onward, the plans are to contain the classes in the exception as well. This would allow you easier, more fine grained control when handling the exception.

Answer to question 2:

The idea behind using dedicated query messages and query handlers is that you can decouple the party who's interested in some data from how you'd implement providing the answer.

Prior to having a dedicated QueryBus and @QueryHandler solution in the framework, what you're suggesting is the only option you have. The notion of using a dedicated query message for this however allows you to having a completely separate (micro) service answering your query without the need for the query-sender to know where that service lives.

Leveraging queries and query handlers in an Axon application is one of the pillars which provides you 'Location Transparency'. You are completely free to not use the querying logic provided by the framework, but I personally think it's a great enabler for evolutionary micro services.

Hope this helps you out Tomáš!

Ensample answered 11/2, 2019 at 9:55 Comment(4)
> From Axon Framework/Server 4.1 onward, the plans are to contain the classes in the exception as well @Ensample - has this been implemented? I am trying to solve the same issue. Thank you.Thrush
The classes are there, in the message though. Hence, not the situation as described here. What you can do however is add your own details Object into the CommandExecutionException, which can be anything. We hover recommend to use a form of status code, essentially taking a similar stance as HTTP status codes.Ensample
To get a feel for how to do this, you can check this video where I show how to do this in Axon 4.4 - youtube.com/watch?v=UcmxyEjbzf4Ensample
Also, this sample might help. It takes a slightly different route, but would achieve the same end result - github.com/AxonIQ/code-samples/tree/master/…Ensample

© 2022 - 2024 — McMap. All rights reserved.