在正确的CQRS实现中,命令是否可以包含数据库实体?

Ale*_*ian 0 cqrs

我还在研究这种模式(即CQRS),我的一位同事开始在我们的小项目中实现它,但坦率地说,我确实对它的实现有一些疑问.

对于初学者我们没有使用任何特定的框架,我们用Java开发了Spring Boot(但这个问题不是特定于编程语言的).您可能会发现另一件非常奇怪的事情是该模式尚未完全实现,只是命令和查询分离概念(没有事件,没有聚合,没有真正的总线).

让我们以自下而上的方式来看看我们做了些什么:

  • 使用ORM映射的数据库实体(Java-Hibernate)
  • 使用Mediator模拟的命令总线
  • 域命令只是不可变对象,应该代表命令本身.

我不想深入了解我们的实现,因为我只想知道以下命令是否可行.

@Data // Lombok annotation
@Entity
class User {
    @Id private Long id;
    private String firstName;
    private String lastName;
    private Integer age;
}

@Value
class EditUserCommand {
    private final User user;
}

@Service
class EditUserCommandHandler extends CommandHandler<EditUserCommand> {
    @Inject
    private UserRepository users;

    @Override
    public void handle(final EditUserCommand command) {
        User old = this.users.findOne(command.getUser().getId());
        User current = command.getUser();
        current.setId(old.getId());

        current = this.users.save(current); 
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我们在项目中实施模式(或者更确切地说是C&Q Separation概念)的方式......但坦率地说?我不知道它是否正确,我发现代码与CQRS模式非常不同.我的问题是:

  • 假设我想对我的实体User进行多次操作,例如我只想重命名他的名字和姓,然后创建一个名为RenameUserFirstAndLastNameCommand的命令; 或者如果我想修改年龄,那么我创建一个名为ModifyUserAgeCommand的命令,是吗?
  • 因此,我不应该像上面所示那样使用整个实体创建命令,但是该命令应该代表域的特定任务,是吗?

在此先感谢您的回复.

Bre*_*tik 6

你是对的.该命令应该只包含执行实际作业所需的最少信息(在ModifyUserAgeCommand的情况下,它只能包含用户ID和新的年龄).在您的处理程序中,您的存储库应负责从数据库中重新保护模型,并应用从命令中包含的信息中适用的任何更改并与存储库保持一致.如果您正在使用DDD,请使用负责用户的聚合来应用更改并保持不变.

顺便说一句,只要您使用单独的类和模型处理读取(查询)和写入(命令),它仍然是CQRS.这通常是一个误解,CQRS必须包括事件来源,最终的一致性,DDD ...恕我直言,它恰好与这些模式很好地协作.