Query
In the CQRS (Command Query Responsibility Segregation) architecture, a Query is the counterpart to a Command. While Commands are used to modify the state of the system, Queries are used to retrieve data.
Queries are designed to work with Projected Entities, which are the results of a Projection process. This ensures that the data returned by Queries is up-to-date and consistent with the projections.
Implementation
-
Query Class:
GetAllQuery
: Represents the data request, holding query parameters and ensuring their validity.
-
Query Handler:
GetAllQueryHandler
: Processes the Query, interacts with the repository to retrieve data, and returns the result.
Query
A Query is a Data Transfer Object (DTO) that contains the information needed to fetch data from the system. It acts as a request for data retrieval and is passed to the QueryHandler.
A Query can be found at: Task/src/useCases/read/ActiveTaskOverview/application/GetAllQuery.ts
.
import { isType } from "is-what";
import { validate } from "uuid";
import { OverwriteProtectionBody, ValidationError } from "@codebricks/codebricks-framework";
export interface GetAllQueryProperties {
}
export class GetAllQuery {
constructor(readonly properties: GetAllQueryProperties) {
this.validate();
}
@OverwriteProtectionBody(false)
validate(): void {
}
}
The GetAllQuery
class is responsible for holding query parameters and ensuring their validity through the validate
method.
Query Handler
The QueryHandler
processes the Query and interacts with the repository to fetch the required data. It converts the Query into a request that the repository can handle and returns the results.
The QueryHandler can be found at: Task/src/useCases/read/ActiveTaskOverview/application/GetAllQueryHandler.ts
.
import { OverwriteProtectionBody } from "@codebricks/codebricks-framework";
import { ActiveTaskOverviewEntity } from "shared/application/readmodels/ActiveTaskOverviewEntity";
import { ActiveTaskOverviewRepository } from "../infrastructure/ActiveTaskOverviewRepository";
import { GetAllQuery } from "./GetAllQuery";
export class GetAllQueryHandler {
constructor(readonly repository: ActiveTaskOverviewRepository = new ActiveTaskOverviewRepository()) {
}
@OverwriteProtectionBody(false)
async handle(query: GetAllQuery): Promise<ActiveTaskOverviewEntity[]> {
const result: ActiveTaskOverviewEntity[] = await this.repository.getAll(
);
return result;
}
}
The GetAllQueryHandler
class is responsible for:
- Receiving the
GetAllQuery
. - Using the repository to fetch the data.
- Returning the results to the caller.
Best Practices
- DTO Usage: Use Query classes as DTOs to encapsulate query parameters and validation logic.
- Separation of Concerns: Keep Query and QueryHandler responsibilities distinct to maintain clarity and manageability.
- Error Handling: Implement robust validation and error handling within Queries and QueryHandlers to ensure data integrity and user-friendly error responses.