Architecture
- CQRS
- Hexagonal
- Microservice
- ...
CQRS
Kacper Walczak · 06-05-2024
Lets learn how to use CQRS in your project.
What is CQRS?
CQRS stands for Command Query Responsibility Segregation.
It is a design pattern that separates the read and write operations of a data source.
Additionally, it allows to use separate Database for reading and writing(what can improve scaling by a lot).
When to use?
- Your business logic is going to evolve/change often
- Your model requires to be read with multiple data projections
- It is required to optimize reads over writes and vice-versa
When to avoid?
- You are building CRUD, don't use there CQRS
- You are starting new project and you are lacking domain experts
- You don't want to introduce any reactive patterns(events/commands/queries needs to be handled and emited by some bus)
Examples
COMMAND
- ApplySalaryIncrease:
And handler for this command:
class ApplySalaryIncreaseAt extends Command { data: {employeeID: string, increasedSalaryValue: number} validAt: Date timestamp = new Date() version = 1 constructor(employeeID: string, increasedSalaryValue: number, validAt: Date) { super({employeeID, increasedSalaryValue}) this.validAt = validAt } }@CommandHandler(ApplySalaryIncreaseAt) class ApplySalaryIncreaseAtHandler implements ICommandHandler<ApplySalaryIncreaseAt> { constructor(private repository: SalariesRepository, private politic: SalariesPolitic) {} async execute(command: ApplySalaryIncreaseAt): Promise<void> { await this.politic.isValidSalaryIncreaseOrThrow({...command.data, validAt: command.validAt}) const accountant = this.repository.getEmployeeAccountant(command.data.employeeID) await accountant.increaseSalaryAt({ newValue: command.data.increasedSalaryValue, validSince: command.validAt }) } }
QUERY
- GetLastEmployeeSalaryIncrease:
And handler for this query:
class GetLastEmployeeSalaryIncrease extends Query { data: {employeeID: string} constructor(employeeID: string) { super({ employeeID }) } }@CommandHandler(GetLastEmployeeSalaryIncrease) class GetLastEmployeeSalaryIncreaseHandler implements IQueryHandler<GetLastEmployeeSalaryIncrease> { constructor(private repository: SalariesRepository) {} async execute(query: GetLastEmployeeSalaryIncrease): Promise<any[]> { return this.repository.getEmployeeLastSalaryIncrease(query.data.employeeID) } }
EVENT
- SalaryIncreased:
Call this event after some logic with:
class SalaryIncreasedEvent { constructor(public readonly employeeID: string) {} }And handler for this event:this.eventBus.publish(new SalaryIncreasedEvent(employeeID));@EventsHandler(SalaryIncreasedEvent) class SalaryIncreasedEventHandler implements IEventHandler<SalaryIncreasedEvent> { constructor(private repository: SalariesRepository, private email: EmailSender) {} async handle(event: SalaryIncreasedEvent): Promise<void> { // do job here, like: // const {employee, accountant, changes} = await repository.fetchLastEmployeeChanges(event.employeeID) // await email.notify(accountant, {employee, changes}) } }
Next
In this article we have learned how to and when utilize CQRS architecture.
Check
Web Architecturesto learn more about different architectures.
Check next:
READ
Latest readings
Readings are sites which will help you with detailed
information about given topic. Read latest ones from Learn.
06-03-2026
Build your own local voice assistant powered by Ollama.
06-03-2026
Generate YouTube thumbnails with FastAPI and Ollama.
05-09-2024
Compare Neo4j and Tigergraph databases, which is easier to work with, etc.