Skip to main content

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.