# How to Implement CQRS Pattern for Command and Query Separation in Java

> **The Command Query Responsibility Segregation (CQRS) pattern splits application logic into two distinct layers: commands that mutate state within transactions, and queries that execute read-only operations returning lightweigh...

- Repository: [Ilkka Seppälä/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
- Tags: 
- Published: 2026-02-27

---

**The Command Query Responsibility Segregation (CQRS) pattern splits application logic into two distinct layers: commands that mutate state within transactions, and queries that execute read-only operations returning lightweight DTOs.**

The `java-design-patterns` repository by iluwatar demonstrates how to implement CQRS pattern for command and query separation through a practical library management example. Located in the `command-query-responsibility-segregation` module, this implementation uses Hibernate to illustrate clear architectural boundaries between write operations (commands) and read operations (queries).

## Core Concepts of CQRS Architecture

CQRS fundamentally separates **commands** (operations that change application state) from **queries** (operations that retrieve data). In the java-design-patterns implementation, this separation manifests as two independent service interfaces: `CommandService` for persistent writes and `QueryService` for optimized reads.

The command side opens Hibernate sessions with explicit transactions to ensure data integrity, while the query side executes HQL or native SQL without transactional overhead, projecting results directly into immutable DTOs. This architectural split enables independent scaling, performance optimization, and simplified maintenance of complex domain models.

## Command Side Implementation (Write Model)

The write model handles all state mutations through the `CommandService` interface defined in [`command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java`](https://github.com/iluwatar/java-design-patterns/blob/main/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java).

### Command Service Contract

The interface declares methods for creating and updating domain entities:

```java
public interface CommandService {
  void authorCreated(String username, String name, String email);
  void bookAddedToAuthor(String title, double price, String username);
  void authorNameUpdated(String username, String name);
  // Additional mutating operations...
}

```

### Command Service Implementation

The `CommandServiceImpl` class in [`command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java) wraps each operation in a Hibernate transaction:

```java
public class CommandServiceImpl implements CommandService {
  private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();

  @Override
  public void authorCreated(String username, String name, String email) {
    var author = new Author(username, name, email);
    try (var session = sessionFactory.openSession()) {
      session.beginTransaction();
      session.save(author);                     // Persist new entity
      session.getTransaction().commit();        // Commit write transaction
    }
  }
  // Additional methods follow: open session → begin → update → commit
}

```

Each command method follows the unit-of-work pattern: opening a session, beginning a transaction, persisting or updating entities, and committing changes. This ensures atomicity for all write operations.

## Query Side Implementation (Read Model)

The read model bypasses domain entities entirely, returning purpose-built DTOs from `com.iluwatar.cqrs.dto`. The `QueryService` interface in [`command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java`](https://github.com/iluwatar/java-design-patterns/blob/main/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java) defines read-only operations:

```java
public interface QueryService {
  Author getAuthorByUsername(String username);
  Book getBook(String title);
  List<Book> getAuthorBooks(String username);
  BigInteger getAuthorBooksCount(String username);
  BigInteger getAuthorsCount();
}

```

### Query Service Implementation

The `QueryServiceImpl` class in [`command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java) executes queries without transactional boundaries:

```java
public class QueryServiceImpl implements QueryService {
  private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();

  @Override
  public Author getAuthorByUsername(String username) {
    try (var session = sessionFactory.openSession()) {
      Query<Author> q = session.createQuery(
        "select new com.iluwatar.cqrs.dto.Author(a.name, a.email, a.username) " +
        "from com.iluwatar.cqrs.domain.model.Author a where a.username=:username");
      q.setParameter(AppConstants.USER_NAME, username);
      return q.uniqueResult();                  // Read-only, no transaction needed
    }
  }
  // Additional methods use native queries for counts or list projections
}

```

Notice the use of constructor expressions in HQL (`select new com.iluwatar.cqrs.dto.Author(...)`) to project domain data directly into DTOs, eliminating the need for entity hydration and reducing memory overhead.

## Wiring Commands and Queries in Application Code

The `App` class in [`command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java`](https://github.com/iluwatar/java-design-patterns/blob/main/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java) demonstrates how to coordinate both services:

```java
public static void main(String[] args) {
  var commands = new CommandServiceImpl();
  commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
  // Additional write operations...

  var queries = new QueryServiceImpl();
  var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
  var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
  LOGGER.info("Author evans : {}", evans);
  LOGGER.info("jBloch books : {}", blochBooks);
}

```

This separation allows the write model to enforce complex business rules and validation while the read model provides denormalized, query-optimized views of the data.

## Key Benefits of CQRS Separation

Implementing CQRS pattern for command and query separation provides several architectural advantages:

- **Independent scalability**: Deploy additional read replicas without impacting write throughput, or scale write nodes independently from query caches.
- **Optimized data models**: The write model uses rich domain entities with encapsulation, while the read model uses flattened DTOs tailored for specific UI requirements.
- **Performance isolation**: Long-running complex queries cannot block transactional writes, and vice versa.
- **Simplified evolution**: Changes to query projections do not affect domain logic, allowing the read model to evolve rapidly without risking write-side integrity.

## Summary

- **Command services** handle state mutations through `CommandServiceImpl`, using Hibernate transactions to persist domain entities.
- **Query services** retrieve data via `QueryServiceImpl`, executing HQL or native SQL to project lightweight DTOs without transactional overhead.
- The `command-query-responsibility-segregation` module in iluwatar/java-design-patterns provides a complete, runnable example using a library domain (authors and books).
- Separation enables independent scaling, performance optimization, and clearer architectural boundaries between read and write concerns.

## Frequently Asked Questions

### What is the difference between Command and Query in CQRS?

Commands are operations that modify application state—such as `authorCreated` or `bookAddedToAuthor`—and execute within transactions to ensure consistency. Queries are read-only operations, such as `getAuthorByUsername` or `getAuthorBooksCount`, that return data without side effects and typically bypass transactional wrappers for performance. In the java-design-patterns implementation, commands persist domain entities while queries return immutable DTOs from `com.iluwatar.cqrs.dto`.

### When should I use CQRS pattern in Java applications?

Use CQRS when your application experiences divergent read and write workloads, requires different optimization strategies for queries versus commands, or when the read model needs to serve multiple presentation formats. The pattern excels in complex domains where write logic involves intricate validation rules, while read operations require denormalized, high-performance projections. Simple CRUD applications with symmetrical read/write patterns may not justify the added architectural complexity.

### How does CQRS handle data consistency between read and write models?

The reference implementation uses a single database with separate service layers, ensuring strong consistency since both sides read from the same underlying tables. In distributed architectures, CQRS often adopts eventual consistency: the write model publishes domain events that asynchronously update read-model projections. The java-design-patterns example demonstrates the simpler synchronous approach, where queries immediately reflect committed command operations.

### Can CQRS work with separate databases for reads and writes?

Yes, though the java-design-patterns example uses a single database, CQRS naturally supports polyglot persistence—using a relational database for transactional writes and a document store or search index for reads. This configuration requires an event-driven synchronization mechanism, typically using domain events from the command side to update the read-side database. The `CommandServiceImpl` could be extended to emit events after `session.getTransaction().commit()`, while `QueryServiceImpl` would query the separate read-optimized data store.