使用 Mediatr 协调查询和命令处理程序

Coordinating Query And Command Handlers with Mediatr

我正在使用 Mediatr 在 dotnet core 3.0 中实现 CQRS 模式。我有一些关于如何协调不同的多个查询和命令的问题。根据我在网上阅读的内容,这里有一些人们描述的最佳实践。

这是我的问题,现在,我正在为 AspNet.Identity 实施 IUserStore<TUser>,假设我们正在查看此示例

    public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        if (user == null) return IdentityResult.Failed(new ArgumentNullError(nameof(user)));
        
        bool userExists = await _mediator.Send(new UserExistsQuery {UserId = user.Id}, cancellationToken);
        
        if (!userExists) return IdentityResult.Failed(new EntityDoesNotExistError(user));
        
        await _mediator.Send(new DeleteUserCommand {User = user}, cancellationToken);
        return IdentityResult.Success;
    }

在这种方法中,我基本上是使用实现用户存储来协调不同的查询和命令以删除用户。在这种情况下,我可以看到执行以下操作以使其尽可能薄并更紧密地遵循 'one command per http request'

  1. 摆脱 UserExistsQueryUserExistsQueryQueryHandler,将该查询移至 DeleteUserCommandHandler 并使用存储库进行查询(UserExistsQueryHandler 现在已经这样做)而不是依赖在查询处理程序上
  2. 而不是 returning Task<Unit> ,return 类似于 IdentityResult

我对执行 #2 犹豫不决的原因是,感觉我正在 return 根据使用命令的上下文来执行某些操作。我正在 returning 一个 IdentityResult 只是因为我在这个实例中需要它。

此外,我之所以将它拆分成这样,首先是为了可重用性。我希望能够进行一些可以在其他地方重复使用的查询和命令。如果我 return 一个 IdentityResult,那么这种做法就违背了目的,因为除了 UserStore<TUser>

我真的不需要它

我一直在阅读有关 IPipelineBehaviour 的内容,但它似乎更像是适用于所有 query/command 处理程序的通用解决方案(即:管道可以是 运行 每个 command/query 如果您的程序集中存在适当的类型)。但是 IPipelineBehaviour 可以用来实现自定义管道吗?在我的例子中,我会将所有逻辑移动到一个管道中,该管道只会 运行 for DeleteUserCommand?

我已经搜索过关于这个主题的文章,但没有真正找到任何有用的东西 - 或者也许我正在搜索错误的术语。我的行话可能有误,但我可以创建仅依赖于 IMediatr 来完成删除用户的协调服务。任何反馈 and/or 阅读 material 将不胜感激。

我认为您误解了 CQRS 的概念。 DeleteAsync 操作本身就是一个命令。因此,如果您需要读取一些数据以继续您的操作,这不是查询,而只是读取操作。因此,每当您需要在命令操作中读取一些数据时,您都必须通过存储库获取它。 请注意,通过使用 CQRS,您可以将用户查询和命令的路径分开,而不是读取和写入。 在命令中,每次写入和读取都必须通过您的领域模型。因此,您的域层中可能存在一个存储库,用于从您的写入数据库中获取数据。但是对于查询,没有必要使用领域模型。 所以你的代码必须如下所示:

public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        _mediator.Send(new DeleteUserCommand(){});       
    }

DeleteUserCommand 处理程序就像:

protected override async Task Handle(DeleteUserCommand request, CancellationToken cancellationToken)
        {
            if (user == null) return IdentityResult.Failed(new ArgumentNullError(nameof(user)));
        
        bool userExists = userRepository.ExistUser(request.UserId);
        
        if (!userExists) return IdentityResult.Failed(new EntityDoesNotExistError(user));
        
        await userRepository.DeleteAsync(request.UserId);
        return IdentityResult.Success;
        }