Query
A request to read the state of the system without changing it, handled by a dedicated handler in a CQRS architecture.
A query is a request to read data from the system without changing its state. It's the read side of CQRS: while a command asks the system to do something, a query asks the system to return something.
Each query has a dedicated handler that knows how to fetch and return the requested data. This is a deliberate separation from the write side. The query handler doesn't go through aggregates or domain objects. It reads directly from whatever storage is most efficient for the given use case, often a read model optimized for that specific view.
In practice, a query is a plain struct describing what data you need:
type GetOrder struct {
OrderUUID string
}
The handler receives this struct and returns the data. It might query a SQL database, hit a cache, or read from a denormalized view. The point is that it's free to use whatever storage and model works best for reading, without being constrained by the write side's data model.
This is where CQRS pays off. The write side can use a normalized relational schema with strict consistency. The read side can use denormalized views, search indexes, or pre-computed projections. Each side evolves independently.
Queries don't have to be complex. In many cases, a query handler is a thin wrapper around a SQL query or a repository call. The value isn't in the handler's complexity but in the clear separation: read logic lives here, write logic lives in command handlers. When your application layer grows, this split makes it easier to maintain and think about the codebase.
References
- How to use basic CQRS in Go — Defines queries as the read side of CQRS: a query should not modify anything, just return data. Walks through splitting an application service into separate command and query handlers, with practical naming conventions and examples.
- Microservices test architecture. Can you sleep well without end-to-end tests? — Discusses testing the application layer split into commands and queries. Notes that most query code just glues layers together, and testing it doesn't always add value unless there's complex orchestration.
- Increasing Cohesion in Go with Generic Decorators — Splits the application layer into commands and queries, CQRS style, and applies generic decorators like ApplyQueryDecorators for cross-cutting concerns such as logging and metrics.
- Synchronous vs Asynchronous Architecture — References CQRS as Command Query Responsibility Segregation and discusses how queries fit into both synchronous and asynchronous architectural approaches.