Member-only story
Implementing CQRS with Spring Modulith
Spring Modulith, a relatively new addition to the Spring ecosystem, provides excellent support for implementing CQRS in a clean, modular way.
In modern enterprise applications, separating read and write concerns often becomes essential as systems grow in complexity. The Command Query Responsibility Segregation (CQRS) pattern addresses this need. When implemented well, this separation can dramatically improve scalability, performance, and maintainability.
In this example, we are going to implement a simple application that handles a Product Catalog.
Let’s dive right in!
1. Project Structure
We need two main modules, one for reads named query
and the other for writes named command
.
Each module will be connected to a different database. (Note: It’s not a prerequisite of CQRS using different databases for read and writes — we could have used a different schema or even just a different table for simplest cases)
The folder structure of our application will look like this:
gae.piaz.modulith.cqrs/
├── command/
│ ├── api/
│ ├── config/
│ ├── domain/
│ ├── events/
│ │ ├── ProductCreatedEvent.java
│ │ ├── ProductUpdatedEvent.java
│ │ ├── ProductReviewEvent.java
│ │ └── package-info.java
│ ├── service/
│ └── package-info.java
├── query/
│ ├── api/
│ ├── application/
│ ├── config/
│ ├── domain/
│ └── package-info.java
├── shared/
│ ├── config/
│ └── package-info.java
└── CQRSApplication.java
The query
folder contains a CLOSED module with reads
business logic
- The
command
folder contains a CLOSED module withwrites
business logic - The
shared
folder contains an OPEN module, with shared configuration
We need to consider CLOSED modules as completely decoupled applications. This means that it should not be possible for the command
module to access any class of the query
module and vice-versa.
The only point of contact is the classes inside the command/event
folder.