MediatR在CleanArchitecture中的应用:CQRS模式实现

MediatR在CleanArchitecture中的应用:CQRS模式实现

【免费下载链接】CleanArchitecture CleanArchitecture 是一个基于.NET Core的应用程序模板项目,遵循干净架构原则。它为软件项目提供了一个清晰的分层结构,有助于分离关注点、提升可维护性和重用性。适合用于构建具有良好架构基础的中大型企业应用。 【免费下载链接】CleanArchitecture 项目地址: https://gitcode.com/GitHub_Trending/cl/CleanArchitecture

引言:为什么选择MediatR和CQRS?

在现代企业级应用开发中,代码的可维护性和可扩展性至关重要。Clean Architecture(干净架构)通过分层设计解决了这一问题,而MediatR与CQRS(Command Query Responsibility Segregation,命令查询职责分离)模式的结合,则为这种架构提供了强大的实现工具。

你是否曾经遇到过:

  • 控制器代码臃肿,包含大量业务逻辑
  • 代码复用困难,相似的逻辑分散在各个地方
  • 单元测试复杂,依赖关系难以mock
  • 系统扩展性差,新增功能需要修改多处代码

本文将深入探讨MediatR在CleanArchitecture项目中的实际应用,展示如何通过CQRS模式构建清晰、可维护的.NET应用架构。

CleanArchitecture项目结构概览

CleanArchitecture采用经典的分层架构:

mermaid

MediatR核心配置

在CleanArchitecture中,MediatR的配置集中在Web项目的配置文件中:

// MediatrConfigs.cs
public static class MediatrConfigs
{
  public static IServiceCollection AddMediatrConfigs(this IServiceCollection services)
  {
    var mediatRAssemblies = new[]
    {
      Assembly.GetAssembly(typeof(Contributor)), // Core层
      Assembly.GetAssembly(typeof(CreateContributorCommand)) // UseCases层
    };

    services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(mediatRAssemblies!))
            .AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>))
            .AddScoped<IDomainEventDispatcher, MediatRDomainEventDispatcher>();

    return services;
  }
}

这种配置方式确保了:

  • 自动发现所有命令和查询处理器
  • 支持跨切割关注点的管道行为
  • 集成领域事件分发机制

CQRS模式实现详解

命令(Command)实现

命令代表改变系统状态的操作,通常对应CREATE、UPDATE、DELETE操作:

// CreateContributorCommand.cs
public record CreateContributorCommand(string Name, string? PhoneNumber) 
    : Ardalis.SharedKernel.ICommand<Result<int>>;

// CreateContributorHandler.cs
public class CreateContributorHandler(IRepository<Contributor> _repository)
  : ICommandHandler<CreateContributorCommand, Result<int>>
{
  public async Task<Result<int>> Handle(CreateContributorCommand request,
    CancellationToken cancellationToken)
  {
    var newContributor = new Contributor(request.Name);
    if (!string.IsNullOrEmpty(request.PhoneNumber))
    {
      newContributor.SetPhoneNumber(request.PhoneNumber);
    }
    var createdItem = await _repository.AddAsync(newContributor, cancellationToken);
    return createdItem.Id;
  }
}

查询(Query)实现

查询代表读取系统状态的操作,不改变系统状态:

// ListContributorsQuery.cs
public record ListContributorsQuery(int? Skip, int? Take) 
    : IQuery<Result<IEnumerable<ContributorDTO>>>;

// ListContributorsHandler.cs
public class ListContributorsHandler(IListContributorsQueryService _query)
  : IQueryHandler<ListContributorsQuery, Result<IEnumerable<ContributorDTO>>>
{
  public async Task<Result<IEnumerable<ContributorDTO>>> Handle(
    ListContributorsQuery request, CancellationToken cancellationToken)
  {
    var result = await _query.ListAsync();
    return Result.Success(result);
  }
}

Web API端点实现

在Web层,端点变得极其简洁,只负责接收请求和返回响应:

// Create.cs - Web API端点
public class Create(IMediator _mediator)
  : Endpoint<CreateContributorRequest, CreateContributorResponse>
{
  public override void Configure()
  {
    Post(CreateContributorRequest.Route);
    AllowAnonymous();
  }

  public override async Task HandleAsync(
    CreateContributorRequest request, CancellationToken cancellationToken)
  {
    var result = await _mediator.Send(
      new CreateContributorCommand(request.Name!, request.PhoneNumber), 
      cancellationToken);

    if (result.IsSuccess)
    {
      Response = new CreateContributorResponse(result.Value, request.Name!);
      return;
    }
  }
}

领域事件处理

CleanArchitecture还集成了领域事件机制,通过MediatR实现事件分发:

// EventDispatchInterceptor.cs - EF Core拦截器
public class EventDispatchInterceptor(IDomainEventDispatcher domainEventDispatcher) 
    : SaveChangesInterceptor
{
  public override async ValueTask<int> SavedChangesAsync(
    SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken)
  {
    var context = eventData.Context;
    if (context is not AppDbContext appDbContext)
    {
      return await base.SavedChangesAsync(eventData, result, cancellationToken);
    }

    // 检索所有有领域事件的跟踪实体
    var entitiesWithEvents = appDbContext.ChangeTracker.Entries<HasDomainEventsBase>()
      .Select(e => e.Entity)
      .Where(e => e.DomainEvents.Any())
      .ToArray();

    // 分发并清除领域事件
    await _domainEventDispatcher.DispatchAndClearEvents(entitiesWithEvents);

    return await base.SavedChangesAsync(eventData, result, cancellationToken);
  }
}

管道行为(Pipeline Behaviors)

MediatR支持管道行为,用于实现横切关注点:

// LoggingBehavior.cs示例
public class LoggingBehavior<TRequest, TResponse>(ILogger<TRequest> logger)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public async Task<TResponse> Handle(TRequest request, 
        RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        logger.LogInformation("Handling {RequestName}", typeof(TRequest).Name);
        var response = await next();
        logger.LogInformation("Handled {RequestName}", typeof(TRequest).Name);
        return response;
    }
}

优势对比分析

特性传统方式MediatR + CQRS方式
控制器复杂度高,包含业务逻辑低,只负责HTTP交互
代码复用性优秀,命令/查询可复用
单元测试复杂,需要mock HTTP上下文简单,直接测试处理器
可维护性随着功能增加急剧下降良好,功能模块化
团队协作容易冲突按功能模块分工明确

最佳实践指南

1. 命令和查询设计原则

mermaid

2. 错误处理策略

// 使用Result模式进行错误处理
public async Task<Result<int>> Handle(CreateContributorCommand request,
    CancellationToken cancellationToken)
{
    try
    {
        // 业务逻辑
        return Result.Success(result);
    }
    catch (Exception ex)
    {
        return Result.Error(ex.Message);
    }
}

3. 性能优化建议

  • 使用IRequest<T>IRequest区分有返回值和无返回值操作
  • 合理使用缓存装饰器模式
  • 避免在查询处理器中进行复杂的数据转换

实际应用场景

场景1:用户注册流程

// RegisterUserCommand.cs
public record RegisterUserCommand(string Email, string Password, string Name) 
    : ICommand<Result<Guid>>;

// RegisterUserHandler.cs
public class RegisterUserHandler : ICommandHandler<RegisterUserCommand, Result<Guid>>
{
    public async Task<Result<Guid>> Handle(RegisterUserCommand request, 
        CancellationToken cancellationToken)
    {
        // 验证邮箱唯一性
        // 密码加密
        // 创建用户实体
        // 发送欢迎邮件
        // 返回用户ID
    }
}

场景2:复杂报表查询

// SalesReportQuery.cs
public record SalesReportQuery(DateTime StartDate, DateTime EndDate, 
    string Region, int? CategoryId) : IQuery<Result<SalesReportDto>>;

// SalesReportHandler.cs
public class SalesReportHandler : IQueryHandler<SalesReportQuery, Result<SalesReportDto>>
{
    public async Task<Result<SalesReportDto>> Handle(SalesReportQuery request, 
        CancellationToken cancellationToken)
    {
        // 多表关联查询
        // 数据聚合计算
        // 生成报表DTO
    }
}

总结与展望

MediatR与CQRS模式在CleanArchitecture中的结合,为.NET应用开发带来了革命性的改进:

  1. 架构清晰度:明确的分层和责任分离
  2. 可测试性:每个命令/查询都可以独立测试
  3. 可维护性:功能模块化,易于理解和修改
  4. 可扩展性:新增功能只需添加新的命令/查询处理器

随着.NET生态的不断发展,这种模式将继续演进,特别是在以下方向:

  • 与微服务架构的更深度集成
  • 云原生应用的支持优化
  • 性能监控和诊断工具的完善

通过本文的深入分析,相信你已经掌握了MediatR在CleanArchitecture中实现CQRS模式的核心要点。在实际项目中应用这些模式,将显著提升你的代码质量和开发效率。

下一步行动建议

  1. 在现有项目中尝试引入MediatR
  2. 从简单的CRUD操作开始实践CQRS
  3. 逐步重构复杂业务逻辑为命令/查询模式
  4. 建立团队内的编码规范和最佳实践

记住,架构的演进是一个持续的过程,关键是找到适合你项目规模和团队能力的平衡点。

【免费下载链接】CleanArchitecture CleanArchitecture 是一个基于.NET Core的应用程序模板项目,遵循干净架构原则。它为软件项目提供了一个清晰的分层结构,有助于分离关注点、提升可维护性和重用性。适合用于构建具有良好架构基础的中大型企业应用。 【免费下载链接】CleanArchitecture 项目地址: https://gitcode.com/GitHub_Trending/cl/CleanArchitecture

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值