In this article, you will find information on:

  • Command query responsibility segregation (CQRS) and how you can apply it in Spring Boot application.

Introduction

According to Wikipedia

Command–query separation (CQS) is a principle of imperative computer programming […] It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer.

CQRS

Command query responsibility segregation (CQRS) applies the CQS principle by using separate Query and Command objects to retrieve and modify data, respectively.

CQRS pattern is often used with Event sourcing architecture

Convention in the examples

As I described in the article I don’t want to throw exceptions if I can avoid it. I will use an object Result which holds result or error that happened during operation.

@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Result<ERROR extends Enum<ERROR>, TYPE> {

  private final ERROR error;
  private final TYPE value;

  public static <ERROR extends Enum<ERROR>, TYPE> Result<ERROR, TYPE> 
      fail(ERROR error) {
    return new Result<>(requireNonNull(error), null);
  }

  public static <ERROR extends Enum<ERROR>, TYPE> Result<ERROR, TYPE> 
      fail(TYPE value, ERROR error) {
    return new Result<>(requireNonNull(error), value);
  }

  public static <ERROR extends Enum<ERROR>, TYPE> Result<ERROR, TYPE> 
      ok(TYPE value) {
    return new Result<>(null, requireNonNull(value));
  }

  //..
}

Implementation of Command

We can define all commands as objects which implements interface below:

public interface Command<INPUT, RESULT> {
    RESULT execute(INPUT input);
}

Example implementation of command can look like:

@Component
@RequiredArgsConstructor
public class CreateAccountCommand 
  implements Command<Account, Result<Error, Account>> {

    private final AccountRepository accountRepository;

    @Transactional
    public Result<Error, Account> execute(Account account) {
        return accountRepository.findById(account.getId())
                .map(a -> Result.fail(a, Error.ACCOUNT_ALREADY_EXIST))
                .orElseGet(() -> Result.ok(accountRepository.save(account)));
    }

    public enum Error {
        ACCOUNT_ALREADY_EXIST
    }

}

Implementation of Query

We can define all query as objects which implements interface below (almost the same as command):

public interface Query<INPUT, RESULT> {

    RESULT execute(INPUT input);

}

Example of implementation can look like:

@Component
@RequiredArgsConstructor
public class GetAccountQuery 
  implements Query<AccountId, Result<Error, Account>> {

    private final AccountRepository accountRepository;

    @Transactional(readOnly = true)
    public Result<Error, Account> execute(AccountId accountId) {
        return accountRepository.findById(accountId)
                .map(Result::<Error, Account>ok)
                .orElse(Result.fail(Error.ACCOUNT_NOT_FOUND));
    }

    public enum Error {
        ACCOUNT_NOT_FOUND
    }
}

Advantages of usage of CQRS

  • By using CQRS you have a clear separation between read and write operations. For example read-only database transactions you mark as read-only. E.g. by using annotation @Transactional(readOnly = true),
  • All operations are separated that fit to Single responsibility principle.
  • When performance is critical, with CQRS, you can optimize read and write sides independently,
  • CQRS may simplify understanding of domain by dividing problem into the command and query parts,
  • You can split work between two teams: one that is implementing commands, second implementing queries,
  • When you already use the Event Sourcing, it combines nicely with CQRS.
  • It is easy to find all occurrences of commands in the code. In fact, you can find all places where you modify data.

Disadvantage of using CQRS

  • I think that CRUD application type can be too simple to use CQRS.

Summary

CQRS is a nice alternative to organize code. It doesn’t need to be used, but can, with event sourcing architecture. I believe that it can make our code cleaner.