-
Notifications
You must be signed in to change notification settings - Fork 3
Query Module
Queries are designed to retrieve data from the system without altering its state. Here's how to utilize the query contracts, handlers, and mediators.
Your queries can be articulated using:
-
IQuery<TQueryResult>
: Represents a query expected to return a result. Every query must have a result.public class RetrieveUserQuery : IQuery<UserDetails> { public int UserId { get; set; } }
-
IStreamQuery<TQueryResult>
: Represents a query that provides a stream of data as the outcome, encapsulated in the formIAsyncEnumerable<TQueryResult>
.public class RetrieveAllUsersQuery : IStreamQuery<UserDetails> { }
Handlers process the queries. Implement the correct handler based on your specific needs:
-
IQueryHandler<TQuery, TQueryResult>
: An asynchronous handler that caters to a specific query and returns a result. The handler can process more derived types of the specifiedTQuery
due to its covariant nature.public class RetrieveUserHandler : IQueryHandler<RetrieveUserQuery, UserDetails> { public Task<UserDetails> HandleAsync(RetrieveUserQuery query, CancellationToken cancellationToken = default) { // Handle and return user details... } }
-
IStreamQueryHandler<TQuery, TQueryResult>
: An asynchronous handler tailored for streaming queries. Returns a stream of data, typically represented asIAsyncEnumerable<TQueryResult>
. This handler also boasts covariant handling capabilities.public class RetrieveAllUsersHandler : IStreamQueryHandler<RetrieveAllUsersQuery, UserDetails> { public IAsyncEnumerable<UserDetails> StreamAsync(RetrieveAllUsersQuery query, CancellationToken cancellationToken = default) { // Stream user details... } }
The IQueryMediator
serves as a bridge between your queries and their corresponding handlers, ensuring that the right handler is invoked for a given query.
-
QueryAsync<TQueryResult>
: This method mediates a regular query to its corresponding handler and retrieves the result. -
StreamAsync<TQueryResult>
: Mediates a streaming query, allowing you to fetch a stream of data from the system.
/// <summary>
/// Mediates a query to its corresponding handler
/// </summary>
public interface IQueryMediator : IRegistrableQueryConstruct
{
Task<TQueryResult> QueryAsync<TQueryResult>(IQuery<TQueryResult> query, CancellationToken cancellationToken = default);
IAsyncEnumerable<TQueryResult> StreamAsync<TQueryResult>(IStreamQuery<TQueryResult> query, CancellationToken cancellationToken = default);
}
Pre-handlers allow operations to be performed before a query gets processed. Covariant handling ensures these pre-handlers can cater to broader query types:
-
IQueryPreHandler<TQuery>
: Triggered before the processing of the designatedTQuery
.public class RetrieveUserPreHandler : IQueryPreHandler<RetrieveUserQuery> { public Task PreHandleAsync(RetrieveUserQuery query, CancellationToken cancellationToken = default) { // Validate or setup prerequisites... } }
-
IQueryPreHandler
: A universal pre-handler that initiates before every query.public class GeneralPreHandler : IQueryPreHandler { public Task PreHandleAsync(IQuery query, CancellationToken cancellationToken = default) { // General validation or setup for all queries... } }
Post-handlers activate after a query's execution, useful for operations like resource cleanup or logging. Covariant handling allows them to cater to more derived query types:
-
IQueryPostHandler<TQuery>
: Activated after the processing of the givenTQuery
.public class RetrieveUserPostHandler : IQueryPostHandler<RetrieveUserQuery> { public Task PostHandleAsync(RetrieveUserQuery query, CancellationToken cancellationToken = default) { // Logging, cleanup, or further processing... } }
-
IQueryPostHandler<TQuery, TQueryResult>
: Designed for a specificTQuery
producing aTQueryResult
outcome.public class RetrieveUserDetailedPostHandler : IQueryPostHandler<RetrieveUserQuery, UserDetails> { public Task PostHandleAsync(RetrieveUserQuery query, UserDetails result, CancellationToken cancellationToken = default) { // Processing based on query and result... } }
These handlers allow interception and handling of errors that might arise during the query processing lifecycle.
-
IQueryErrorHandler<TQuery>
: Triggered for a specific query encountering an error.public class RetrieveUserErrorHandler : IQueryErrorHandler<RetrieveUserQuery> { public Task HandleErrorAsync(RetrieveUserQuery query, Exception exception, CancellationToken cancellationToken = default) { // Handle error for RetrieveUserQuery... } }
-
IQueryErrorHandler<TQuery, TQueryResult>
: Activated when a specificTQuery
targeting aTQueryResult
runs into an error.public class RetrieveUserDetailedErrorHandler : IQueryErrorHandler<RetrieveUserQuery, UserDetails> { public Task HandleErrorAsync(RetrieveUserQuery query, UserDetails result, Exception exception, CancellationToken cancellationToken = default) { // Handle error based on query and result... } }
-
IQueryErrorHandler
: Acts as a catch-all error handler for all queries.public class GeneralErrorHandler : IQueryErrorHandler { public Task HandleErrorAsync(IQuery query, object result, Exception exception, CancellationToken cancellationToken = default) { // General error handling for all queries... } }
- Command Contracts
- Command Handler Contracts
- Command Main Handlers
- Command Mediator
- Command Pre Handlers
- Command Post-Handlers
- Command Error Handlers
- Query Contracts
- Query Handler Contracts
- Query Main Handlers
- Query Mediator
- Query Pre Handlers
- Query Post-Handlers
- Query Error Handlers