@QueryHandler

In the realm of CQRS (Command Query Responsibility Segregation), projections play a crucial role in serving data for queries. This chapter dives into the @QueryHandler annotation used within projections to define methods that handle incoming queries efficiently.

Understanding @QueryHandler

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Handler
public @interface QueryHandler {
}

The @QueryHandler annotation identifies a method within your projection class as a query handler. This method is responsible for processing a specific query object, retrieving relevant data from the projection state, and constructing the corresponding response.

Here's a breakdown of the annotation's definition:

  • @Retention(RetentionPolicy.RUNTIME): Ensures the annotation information is retained at runtime, allowing Evento to discover and execute these methods when queries arrive.

  • @Target(ElementType.METHOD): Specifies that the annotation can only be applied to method declarations within your projection class.

  • @Handler (Optional): In some frameworks like Evento, @QueryHandler might inherit from a base annotation like @Handler for consistency in marking different handler types.

Structure of a @QueryHandler Method

A well-structured @QueryHandler method typically adheres to the following pattern:

  1. Query Object as First Parameter: The first parameter of the method must be a subtype of the Query class representing the specific type of query being handled (e.g., DemoViewFindByIdQuery).

  2. Optional Additional Parameters: Depending on your specific needs, you might include additional parameters like:

    • QueryMessage<Query> (Optional): This provides access to details about the received query message, including metadata and timestamp.

    • QueryGateway (Optional): This allows implementing the federated query pattern within your query handler (discussed in a later chapter).

    • Metadata (Optional): This represents metadata associated with the query (often already included in the QueryMessage object).

    • Instant (Optional): This represents the timestamp of the query message.

  3. Single or Multiple Return Type: The return type of the @QueryHandler method can be either Single<View> or Multiple<View>.

    • Single<View>: Used for queries that expect a single response object of a specific view type (e.g., DemoView).

    • Multiple<View>: Used for queries that expect a collection of view objects of a specific type (e.g., retrieving a list of DemoView objects).

Example: @QueryHandler in Action

@QueryHandler
Single<DemoView> query(DemoViewFindByIdQuery query,
                       QueryMessage<DemoViewFindByIdQuery> queryMessage,
                       QueryGateway queryGateway,
                       Metadata metadata,
                       Instant instant) {
    Utils.logMethodFlow(this, "query", query, "BEGIN");
    var result = repository.findById(query.getDemoId())
            .filter(d -> d.getDeletedAt() == null)
            .map(Demo::toDemoView).orElseThrow();
    result.setDemoId(query.getDemoId());
    Utils.logMethodFlow(this, "query", query, "END");
    return Single.of(result);
}

The provided code example showcases two @QueryHandler methods within a DemoProjection class:

  • Both methods handle queries related to DemoView:

    • query(DemoViewFindByIdQuery, ...) handles finding a DemoView by its ID.

    • query(DemoViewFindAllQuery) handles retrieving all DemoView objects.

  • The methods retrieve data from the DemoRepository based on the query type, filter out deleted entries, transform the data to DemoView objects, and construct the appropriate response (Single or Multiple).

Key Takeaways

  • @QueryHandler empowers you to define methods within projections that act as handlers for specific queries.

  • These methods retrieve data from the projection state and transform it into a format suitable for the query response.

  • Understanding @QueryHandler is crucial for building effective projections that efficiently handle queries within your CQRS application.

Optional Parameters and Advanced Topics:

While the core structure focuses on the query object and response, the additional optional parameters (QueryMessage, QueryGateway, Metadata, and Instant) provide flexibility for more advanced scenarios. These parameters are covered in detail in separate chapters focusing on federated queries, metadata handling, and timestamping within Evento.

Last updated