Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Axon @QueryHandler with Spring @ExceptionHandler

Tags:

spring

axon

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)));   
}
like image 318
Tomáš Mika Avatar asked Feb 08 '19 08:02

Tomáš Mika


1 Answers

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áš!

like image 135
Steven Avatar answered Oct 31 '22 21:10

Steven