Apply CQRS to a Spring REST API – 将CQRS应用于Spring REST API

最后修改: 2015年 9月 10日


1. Overview


In this quick article, we’re going to do something new. We’re going to evolve an existing REST Spring API and make it use Command Query Responsibility Segregation – CQRS.

在这篇快速文章中,我们将做一些新的事情。我们将对现有的REST Spring API进行改进,使其使用命令查询责任隔离–CQRS

The goal is to clearly separate both the service and the controller layers to deal with Reads – Queries and Writes – Commands coming into the system separately.


Keep in mind that this is just an early first step towards this kind of architecture, not “an arrival point”. That being said – I’m excited about this one.

请记住,这只是迈向这种架构的早期第一步,而不是 “一个到达点”。既然如此–我对这个项目感到兴奋。

Finally – the example API we’re going to be using is publishing User resources and is part of our ongoing Reddit app case study to exemplify how this works – but of course, any API will do.


2. The Service Layer


We’ll start simple – by just identifying the read and the write operations in our previous User service – and we’ll split that into 2 separate services – UserQueryService and UserCommandService:


public interface IUserQueryService {

    List<User> getUsersList(int page, int size, String sortDir, String sort);

    String checkPasswordResetToken(long userId, String token);

    String checkConfirmRegistrationToken(String token);

    long countAllUsers();

public interface IUserCommandService {

    void registerNewUser(String username, String email, String password, String appUrl);

    void updateUserPassword(User user, String password, String oldPassword);

    void changeUserPassword(User user, String password);

    void resetPassword(String email, String appUrl);

    void createVerificationTokenForUser(User user, String token);

    void updateUser(User user);


From reading this API you can clearly see how the query service is doing all the reading and the command service isn’t reading any data – all void returns.


3. The Controller Layer


Next up – the controller layer.


3.1. The Query Controller


Here is our UserQueryRestController:


@RequestMapping(value = "/api/users")
public class UserQueryRestController {

    private IUserQueryService userService;

    private IScheduledPostQueryService scheduledPostService;

    private ModelMapper modelMapper;

    @RequestMapping(method = RequestMethod.GET)
    public List<UserQueryDto> getUsersList(...) {
        PagingInfo pagingInfo = new PagingInfo(page, size, userService.countAllUsers());
        response.addHeader("PAGING_INFO", pagingInfo.toString());
        List<User> users = userService.getUsersList(page, size, sortDir, sort);
          user -> convertUserEntityToDto(user)).collect(Collectors.toList());

    private UserQueryDto convertUserEntityToDto(User user) {
        UserQueryDto dto =, UserQueryDto.class);
        return dto;

What’s interesting here is that the query controller is only injecting query services.


What would be even more interesting is to cut off the access of this controller to the command services – by placing these in a separate module.


3.2. The Command Controller


Now, here’s our command controller implementation:


@RequestMapping(value = "/api/users")
public class UserCommandRestController {

    private IUserCommandService userService;

    private ModelMapper modelMapper;

    @RequestMapping(value = "/registration", method = RequestMethod.POST)
    public void register(
      HttpServletRequest request, @RequestBody UserRegisterCommandDto userDto) {
        String appUrl = request.getRequestURL().toString().replace(request.getRequestURI(), "");
          userDto.getUsername(), userDto.getEmail(), userDto.getPassword(), appUrl);

    @RequestMapping(value = "/password", method = RequestMethod.PUT)
    public void updateUserPassword(@RequestBody UserUpdatePasswordCommandDto userDto) {
          getCurrentUser(), userDto.getPassword(), userDto.getOldPassword());

    @RequestMapping(value = "/passwordReset", method = RequestMethod.POST)
    public void createAResetPassword(
      HttpServletRequest request, 
      @RequestBody UserTriggerResetPasswordCommandDto userDto) 
        String appUrl = request.getRequestURL().toString().replace(request.getRequestURI(), "");
        userService.resetPassword(userDto.getEmail(), appUrl);

    @RequestMapping(value = "/password", method = RequestMethod.POST)
    public void changeUserPassword(@RequestBody UserchangePasswordCommandDto userDto) {
        userService.changeUserPassword(getCurrentUser(), userDto.getPassword());

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public void updateUser(@RequestBody UserUpdateCommandDto userDto) {

    private User convertToEntity(UserUpdateCommandDto userDto) {
        return, User.class);

A few interesting things are happening here. First – notice how each of these API implementations is using a different command. This is mainly to give us a good base for further improving the design of the API and extracting different resources as they emerge.


Another reason is that when we take the next step, towards Event Sourcing – we have a clean set of commands that we’re working with.


3.3. Separate Resource Representations


Let’s now quickly go over the different representations of our User resource, after this separation into commands and queries:


public class UserQueryDto {
    private Long id;

    private String username;

    private boolean enabled;

    private Set<Role> roles;

    private long scheduledPostsCount;

Here are our Command DTOs:


  • UserRegisterCommandDto used to represent user registration data:
public class UserRegisterCommandDto {
    private String username;
    private String email;
    private String password;
  • UserUpdatePasswordCommandDto used to represent data to update current user password:
public class UserUpdatePasswordCommandDto {
    private String oldPassword;
    private String password;
  • UserTriggerResetPasswordCommandDto used to represent user’s email to trigger reset password by sending an email with reset password token:
public class UserTriggerResetPasswordCommandDto {
    private String email;
  • UserChangePasswordCommandDto used to represent new user password – this command is called after user use password reset token.
public class UserChangePasswordCommandDto {
    private String password;
  • UserUpdateCommandDto used to represent new user’s data after modifications:
public class UserUpdateCommandDto {
    private Long id;

    private boolean enabled;

    private Set<Role> roles;

4. Conclusion


In this tutorial, we laid the groundwork towards a clean CQRS implementation for a Spring REST API.

在本教程中,我们为Spring REST API的一个干净的CQRS实现奠定了基础。

The next step will be to keep improving the API by identifying some separate responsibilities (and Resources) out into their own services so that we more closely align with a Resource-centric architecture.