我是事件源的新手,但正如我所理解的,当我们有一个命令用例时,我们在内存中实例化一个聚合,从事件存储中将事件应用到它,以便处于正确的状态,进行适当的更改,然后将这些更改存储回事件存储。我们还有一个读模型存储,它最终会被这些更改所更新。
在我的情况下,我有一个CreateUserUseCase
(这是一个命令用例),我想先检查用户是否已经存在,如果用户名已经采取。例如:
const userAlreadyExists = await this.userRepo.exists(email);
if (userAlreadyExists) {
return new EmailAlreadyExistsError(email);
}
const alreadyCreatedUserByUserName = await this.userRepo
.getUserByUserName(username);
if (alreadyCreatedUserByUserName) {
return new UsernameTakenError(username);
}
const user = new User(username, password, email);
await this.userRepo.save(user);
因此,对于save
方法,我将使用事件存储并将未提交的事件附加到其中。那么exists
和getUserByUserName
方法呢?一方面,我想做一个特定的查询,这样我就可以使用我的读取模型存储来获取我需要的数据,但另一方面,这与CQRS产生了矛盾。在这种情况下我们该怎么做呢?我们是否以某种方式对事件存储执行查询?我们怎么做呢?
提前感谢!
CQRS不应该被解释为"不要查询写模型";(因为为了命令处理的目的,从写模型确定状态的过程需要查询,所以这个限制是站不住脚的)。相反,可以将其解释为"对于查询使用不同的数据模型而不是用于处理更新意图的数据模型",这是完全可以接受的。这个公式意味着,如果写模型非常适合给定的查询,则可以针对写模型执行查询。
事件溯源反过来又被认为是(特别是与某些使用风格结合在一起)数据模型中针对写和读进行优化的最终结果,因此事件溯源模型使得几乎所有的查询在相当小的集合之外都非常低效,因此需要某种形式的CQRS。
事件存储包含的查询工具通常是有限的,但是任何适合事件存储的查询(因为它需要重播事件)都是一个复合查询,相当于"给我该实体的最新快照和(如果快照存在)该快照之后的第一个n个事件,或者(如果没有快照)该实体的第一个n个事件"。该查询的结果对"这个实体是否发布了事件"的问题是决定性的(对保留等东西进行模量)?