最佳实践
🎯 MediatR 开发的行业最佳实践和规范
📖 概述
遵循最佳实践可以确保 MediatR 应用的可维护性、性能和可扩展性。本章总结了来自实际项目的经验和教训。
📝 命名约定
请求命名
csharp
// ✅ 推荐:使用 Command/Query 后缀
public class CreateOrderCommand : IRequest<OrderResult> { }
public class GetOrderQuery : IRequest<OrderDto> { }
public class UpdateUserCommand : IRequest { }
public class DeleteProductCommand : IRequest { }
// ❌ 避免:模糊的命名
public class OrderRequest : IRequest<OrderResult> { } // 不清楚是命令还是查询
public class UserData : IRequest<UserDto> { } // 不明确Handler 命名
csharp
// ✅ 推荐:与请求对应
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult> { }
public class GetOrderHandler : IRequestHandler<GetOrderQuery, OrderDto> { }
// ❌ 避免:通用命名
public class OrderHandler : IRequestHandler<CreateOrderCommand, OrderResult> { } // 不清晰
public class Handler : IRequestHandler<GetOrderQuery, OrderDto> { } // 太泛通知命名
csharp
// ✅ 推荐:使用 Event/Notification 后缀
public class OrderCreatedEvent : INotification { }
public class UserRegisteredNotification : INotification { }
public class PaymentProcessedEvent : INotification { }
// ❌ 避免
public class OrderMessage : INotification { } // 不清楚管道行为命名
csharp
// ✅ 推荐:使用 Behavior 后缀
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { }
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { }
public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { }🎯 Handler 单一职责原则
✅ 正确做法
csharp
// 每个 Handler 只处理一种请求
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
{
// 只负责创建订单
var order = new Order { /* ... */ };
await _repo.AddAsync(order, ct);
return new OrderResult { OrderId = order.Id };
}
}
public class SendOrderConfirmationHandler : INotificationHandler<OrderCreatedEvent>
{
public async Task Handle(OrderCreatedEvent notification, CancellationToken ct)
{
// 只负责发送邮件
await _emailService.SendAsync(notification.CustomerEmail);
}
}❌ 错误做法
csharp
// 一个 Handler 处理多种职责
public class OrderHandler :
IRequestHandler<CreateOrderCommand>,
IRequestHandler<UpdateOrderCommand>,
INotificationHandler<OrderCreatedEvent>
{
// 违反单一职责原则,难以维护和测试
}🔧 避免在 Handler 中直接依赖多个服务
❌ 反模式
csharp
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
private readonly IOrderRepository _orderRepo;
private readonly ICustomerRepository _customerRepo;
private readonly IProductRepository _productRepo;
private readonly IPaymentService _paymentService;
private readonly IEmailService _emailService;
private readonly IInventoryService _inventoryService;
private readonly IAuditService _auditService;
private readonly ILogger<CreateOrderHandler> _logger;
// 构造函数参数过多(8个)
public CreateOrderHandler(
IOrderRepository orderRepo,
ICustomerRepository customerRepo,
IProductRepository productRepo,
IPaymentService paymentService,
IEmailService emailService,
IInventoryService inventoryService,
IAuditService auditService,
ILogger<CreateOrderHandler> logger)
{
// ...
}
}问题:
- 违反单一职责原则
- 难以测试
- 耦合度高
✅ 推荐做法:引入服务层
csharp
// 提取领域服务
public class OrderProcessingService
{
private readonly IOrderRepository _orderRepo;
private readonly IPaymentService _paymentService;
private readonly IInventoryService _inventoryService;
public OrderProcessingService(
IOrderRepository orderRepo,
IPaymentService paymentService,
IInventoryService inventoryService)
{
_orderRepo = orderRepo;
_paymentService = paymentService;
_inventoryService = inventoryService;
}
public async Task<Order> ProcessOrder(CreateOrderCommand command, CancellationToken ct)
{
var order = await CreateOrder(command, ct);
await _paymentService.ProcessAsync(order.Id, ct);
await _inventoryService.ReserveAsync(order.Items, ct);
return order;
}
}
// Handler 变得简洁
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
private readonly OrderProcessingService _orderProcessingService;
private readonly IMediator _mediator;
public CreateOrderHandler(
OrderProcessingService orderProcessingService,
IMediator mediator)
{
_orderProcessingService = orderProcessingService;
_mediator = mediator;
}
public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
{
var order = await _orderProcessingService.ProcessOrder(request, ct);
// 发布事件
await _mediator.Publish(new OrderCreatedEvent
{
OrderId = order.Id
}, ct);
return new OrderResult { OrderId = order.Id };
}
}优势:
- ✅ Handler 职责清晰
- ✅ 领域服务可复用
- ✅ 易于测试
- ✅ 符合 DDD 原则
🔄 管道行为的设计原则
原则 1:无状态
csharp
// ✅ 正确:无状态行为
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger; // 只注入依赖,不保存状态
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
// 不使用实例字段保存请求/响应数据
_logger.LogInformation("Processing: {Request}", request);
return await next(ct);
}
}
// ❌ 错误:有状态行为
public class BadBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private List<string> _requestHistory = new(); // ❌ 保存状态,线程不安全
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
_requestHistory.Add(request.ToString()); // ❌ 竞态条件
return await next(ct);
}
}原则 2:幂等性
csharp
// ✅ 正确:幂等行为(多次执行结果相同)
public class IdempotentCachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
var cacheKey = GenerateCacheKey(request);
if (_cache.TryGetValue(cacheKey, out TResponse cached))
return cached;
var response = await next(ct);
_cache.Set(cacheKey, response);
return response; // 无论执行多少次,结果一致
}
}原则 3:快速失败
csharp
// ✅ 正确:验证失败立即返回
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
var validationResult = await _validator.ValidateAsync(request, ct);
if (!validationResult.IsValid)
{
// 快速失败,不执行后续逻辑
throw new ValidationException(validationResult.Errors);
}
return await next(ct);
}
}⚠️ 异常处理策略
哪些在 Behavior 中处理
csharp
// ✅ 在 Behavior 中处理横切关注点的异常
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
try
{
var validationResult = await _validator.ValidateAsync(request, ct);
if (!validationResult.IsValid)
throw new ValidationException(validationResult.Errors); // 验证异常
return await next(ct);
}
catch (ValidationException)
{
throw; // 重新抛出,让全局处理器处理
}
}
}哪些在 Handler 中处理
csharp
// ✅ 在 Handler 中处理业务逻辑异常
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
{
// 业务规则验证
if (request.Quantity > MaxQuantity)
throw new BusinessException("超过最大购买数量"); // 业务异常
// 领域逻辑
var order = new Order { /* ... */ };
// 持久化异常由事务行为处理
await _repo.AddAsync(order, ct);
return new OrderResult { OrderId = order.Id };
}
}全局异常处理
csharp
// 中间件中的全局异常处理
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
var exception = contextFeature?.Error;
context.Response.StatusCode = exception switch
{
ValidationException => 400,
NotFoundException => 404,
BusinessException => 422,
_ => 500
};
await context.Response.WriteAsJsonAsync(new
{
error = exception?.Message,
type = exception?.GetType().Name
});
});
});🚫 避免过度使用 MediatR
何时使用 MediatR
✅ 适合的场景:
- 复杂的业务逻辑
- 需要解耦的模块
- CQRS 架构
- 领域事件驱动
- 团队协作的大型项目
- 需要横切关注点(日志、验证、事务)
何时不使用 MediatR
❌ 不适合的场景:
1. 简单 CRUD
csharp
// ❌ 过度设计:简单的查询
public class GetUserQuery : IRequest<UserDto>
{
public Guid UserId { get; set; }
}
public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken ct)
{
return await _dbContext.Users
.Where(u => u.Id == request.UserId)
.Select(u => new UserDto { /* ... */ })
.FirstOrDefaultAsync(ct);
}
}
// ✅ 更简单:直接调用
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(Guid id)
{
var user = await _dbContext.Users
.Where(u => u.Id == id)
.Select(u => new UserDto { /* ... */ })
.FirstOrDefaultAsync();
return user is null ? NotFound() : Ok(user);
}2. 性能极度敏感
csharp
// ❌ 高频交易场景(纳秒级要求)
for (int i = 0; i < 1_000_000; i++)
{
await _mediator.Send(new TradeCommand()); // 100ns 开销累积
}
// ✅ 直接调用
for (int i = 0; i < 1_000_000; i++)
{
await _tradingService.ExecuteTrade(); // 零开销
}3. 小型个人项目
// 如果项目只有几个页面,引入 MediatR 可能增加不必要的复杂度📋 文档与团队规范
代码注释规范
csharp
/// <summary>
/// 创建订单命令
/// </summary>
public class CreateOrderCommand : IRequest<OrderResult>
{
/// <summary>
/// 客户ID
/// </summary>
public Guid CustomerId { get; set; }
/// <summary>
/// 订单项列表
/// </summary>
public List<OrderItemCommand> Items { get; set; }
}
/// <summary>
/// 创建订单处理器
/// </summary>
/// <remarks>
/// 负责协调订单创建、支付处理和库存预留
/// </remarks>
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
/// <summary>
/// 处理创建订单请求
/// </summary>
/// <param name="request">创建订单命令</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>订单结果</returns>
/// <exception cref="ValidationException">当参数验证失败时</exception>
/// <exception cref="BusinessException">当业务规则违反时</exception>
public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
// 实现...
}
}团队规范文档
markdown
# MediatR 开发规范
## 1. 命名规范
- 命令以 `Command` 结尾
- 查询以 `Query` 结尾
- Handler 以 `Handler` 结尾
- 事件以 `Event` 或 `Notification` 结尾
## 2. 文件组织
- 每个 Command/Query 单独文件夹
- Handler 与请求放在同一文件夹
- Validator 与请求放在同一文件夹
## 3. 代码审查清单
- [ ] Handler 是否单一职责
- [ ] 是否使用了管道行为处理横切关注点
- [ ] 是否有适当的异常处理
- [ ] 是否编写了单元测试
- [ ] 是否符合命名规范🎯 最佳实践总结
Do's ✅
- ✅ 使用清晰的命名约定
- ✅ 保持 Handler 单一职责
- ✅ 使用管道行为处理横切关注点
- ✅ 引入领域服务简化 Handler
- ✅ 编写单元测试
- ✅ 使用异步 API
- ✅ 合理缓存
- ✅ 记录详细的日志
- ✅ 遵循团队规范
- ✅ 文档化复杂逻辑
Don'ts ❌
- ❌ 不要在 Handler 中注入过多依赖
- ❌ 不要在有状态的行为中保存数据
- ❌ 不要吞掉异常
- ❌ 不要在简单 CRUD 中使用 MediatR
- ❌ 不要使用同步阻塞操作
- ❌ 不要让 Handler 处理多种请求
- ❌ 不要在 Behavior 中执行业务逻辑
- ❌ 不要忽略性能监控
- ❌ 不要跳过单元测试
- ❌ 不要过度设计
🎓 总结
遵循这些最佳实践可以确保:
✅ 代码质量高 - 清晰、可维护
✅ 性能优秀 - 高效、可扩展
✅ 易于测试 - 隔离、可靠
✅ 团队协作顺畅 - 规范、一致
💡 提示: 最佳实践不是教条,要根据项目实际情况灵活应用!