简单三层架构引入 MediatR
🔄 将传统三层架构重构为 MediatR 模式
📖 场景描述
假设你有一个传统的三层架构应用:
TraditionalApp/
├── Controllers/
│ └── OrdersController.cs
├── Services/
│ └── OrderService.cs
├── Repositories/
│ └── OrderRepository.cs
└── Models/
└── Order.cs问题:
- ❌ 控制器直接依赖多个服务
- ❌ 服务之间相互调用,耦合严重
- ❌ 横切关注点(日志、验证)分散在各处
- ❌ 难以测试和维护
目标:使用 MediatR 重构,实现解耦和标准化。
🔧 改造步骤
步骤 1:安装 MediatR
bash
dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
dotnet add package FluentValidation.AspNetCore步骤 2:重构前 - 传统三层架构
控制器(改造前)
csharp
// Controllers/OrdersController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly OrderService _orderService;
private readonly PaymentService _paymentService;
private readonly EmailService _emailService;
private readonly InventoryService _inventoryService;
private readonly ILogger<OrdersController> _logger;
public OrdersController(
OrderService orderService,
PaymentService paymentService,
EmailService emailService,
InventoryService inventoryService,
ILogger<OrdersController> logger)
{
_orderService = orderService;
_paymentService = paymentService;
_emailService = emailService;
_inventoryService = inventoryService;
_logger = logger;
}
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] OrderRequest request)
{
_logger.LogInformation("开始创建订单: {ProductId}", request.ProductId);
try
{
// 验证
if (request.Quantity <= 0)
return BadRequest("数量必须大于0");
// 创建订单
var order = await _orderService.CreateOrder(request);
// 处理支付
await _paymentService.ProcessPayment(order.Id);
// 更新库存
await _inventoryService.UpdateStock(order.Items);
// 发送邮件
await _emailService.SendConfirmation(order.CustomerEmail);
_logger.LogInformation("订单创建成功: {OrderId}", order.Id);
return Ok(new { OrderId = order.Id });
}
catch (Exception ex)
{
_logger.LogError(ex, "订单创建失败");
return StatusCode(500, "服务器错误");
}
}
}问题:
- 控制器职责过多(验证、协调、日志)
- 紧耦合(依赖4个服务)
- 难以测试
- 代码重复(每个方法都要注入所有服务)
步骤 3:重构后 - MediatR 架构
1. 定义命令
csharp
// Commands/CreateOrderCommand.cs
public class CreateOrderCommand : IRequest<OrderResult>
{
public Guid CustomerId { get; set; }
public List<OrderItemCommand> Items { get; set; }
public string ShippingAddress { get; set; }
}
public class OrderItemCommand
{
public Guid ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
public class OrderResult
{
public Guid OrderId { get; set; }
public decimal TotalAmount { get; set; }
}2. 实现处理器
csharp
// Commands/CreateOrderHandler.cs
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
private readonly IOrderRepository _orderRepo;
private readonly IPaymentService _paymentService;
private readonly IInventoryService _inventoryService;
private readonly IEmailService _emailService;
private readonly IMediator _mediator;
public CreateOrderHandler(
IOrderRepository orderRepo,
IPaymentService paymentService,
IInventoryService inventoryService,
IEmailService emailService,
IMediator mediator)
{
_orderRepo = orderRepo;
_paymentService = paymentService;
_inventoryService = inventoryService;
_emailService = emailService;
_mediator = mediator;
}
public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
{
// 1. 创建订单
var order = new Order
{
Id = Guid.NewGuid(),
CustomerId = request.CustomerId,
Items = request.Items.Select(i => new OrderItem
{
ProductId = i.ProductId,
Quantity = i.Quantity,
Price = i.Price
}).ToList(),
ShippingAddress = request.ShippingAddress,
CreatedAt = DateTime.UtcNow
};
await _orderRepo.AddAsync(order, ct);
// 2. 处理支付
await _paymentService.ProcessPayment(order.Id, ct);
// 3. 更新库存
await _inventoryService.UpdateStock(order.Items, ct);
// 4. 发布事件(替代直接调用邮件服务)
await _mediator.Publish(new OrderCreatedNotification
{
OrderId = order.Id,
CustomerEmail = order.CustomerEmail,
TotalAmount = order.TotalAmount
}, ct);
return new OrderResult
{
OrderId = order.Id,
TotalAmount = order.TotalAmount
};
}
}3. 简化控制器
csharp
// Controllers/OrdersController.cs(重构后)
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
public OrdersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<ActionResult<OrderResult>> CreateOrder([FromBody] CreateOrderCommand command)
{
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetOrder), new { id = result.OrderId }, result);
}
[HttpGet("{id}")]
public async Task<ActionResult<OrderDto>> GetOrder(Guid id)
{
var query = new GetOrderQuery { OrderId = id };
var order = await _mediator.Send(query);
return Ok(order);
}
}改进:
- ✅ 控制器只负责 HTTP 层面
- ✅ 只依赖一个 IMediator
- ✅ 易于测试
- ✅ 代码简洁
步骤 4:添加管道行为
日志行为
csharp
// Behaviors/LoggingBehavior.cs
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
var requestName = typeof(TRequest).Name;
_logger.LogInformation("处理请求: {RequestName}", requestName);
var stopwatch = Stopwatch.StartNew();
try
{
var response = await next();
stopwatch.Stop();
_logger.LogInformation("请求完成: {RequestName}, 耗时: {ElapsedMilliseconds}ms",
requestName, stopwatch.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "请求失败: {RequestName}, 耗时: {ElapsedMilliseconds}ms",
requestName, stopwatch.ElapsedMilliseconds);
throw;
}
}
}验证行为
csharp
// Commands/CreateOrderValidator.cs
public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>
{
public CreateOrderCommandValidator()
{
RuleFor(x => x.CustomerId)
.NotEmpty().WithMessage("客户ID不能为空");
RuleFor(x => x.Items)
.NotEmpty().WithMessage("订单项不能为空")
.Must(items => items.All(i => i.Quantity > 0))
.WithMessage("商品数量必须大于0");
RuleFor(x => x.ShippingAddress)
.NotEmpty().WithMessage("收货地址不能为空");
}
}
// Behaviors/ValidationBehavior.cs
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
if (_validators.Any())
{
var context = new ValidationContext<TRequest>(request);
var results = await Task.WhenAll(
_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
var failures = results.SelectMany(r => r.Errors).Where(f => f != null).ToList();
if (failures.Count != 0)
throw new ValidationException(failures);
}
return await next();
}
}注册行为
csharp
// Program.cs
builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
builder.Services.AddValidatorsFromAssembly(typeof(Program).Assembly);
// 注册管道行为
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));步骤 5:使用通知解耦
定义通知
csharp
// Notifications/OrderCreatedNotification.cs
public class OrderCreatedNotification : INotification
{
public Guid OrderId { get; set; }
public string CustomerEmail { get; set; }
public decimal TotalAmount { get; set; }
}实现多个处理器
csharp
// Notifications/SendEmailHandler.cs
public class SendEmailHandler : INotificationHandler<OrderCreatedNotification>
{
private readonly IEmailService _emailService;
public async Task Handle(OrderCreatedNotification notification, CancellationToken ct)
{
await _emailService.SendConfirmationAsync(notification.CustomerEmail);
}
}
// Notifications/UpdateAnalyticsHandler.cs
public class UpdateAnalyticsHandler : INotificationHandler<OrderCreatedNotification>
{
private readonly IAnalyticsService _analyticsService;
public async Task Handle(OrderCreatedNotification notification, CancellationToken ct)
{
await _analyticsService.TrackEventAsync("order_created", new
{
notification.OrderId,
notification.TotalAmount
});
}
}📊 改造前后对比
代码对比
| 维度 | 改造前 | 改造后 | 改进 |
|---|---|---|---|
| 控制器依赖 | 5个服务 | 1个 IMediator | ✅ 减少 80% |
| 控制器行数 | ~50行 | ~10行 | ✅ 减少 80% |
| 业务逻辑位置 | 控制器 | Handler | ✅ 职责清晰 |
| 日志记录 | 手动编写 | 自动应用 | ✅ DRY |
| 参数验证 | 分散在各处 | 统一行为 | ✅ 一致性 |
| 可扩展性 | 修改控制器 | 新增处理器 | ✅ 开闭原则 |
| 单元测试 | 复杂(Mock 5个服务) | 简单(Mock 1个) | ✅ 易测试 |
🎯 改造收益
1. 代码质量提升
csharp
// 改造前:50行
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] OrderRequest request)
{
_logger.LogInformation(...);
if (request.Quantity <= 0) return BadRequest(...);
var order = await _orderService.CreateOrder(request);
await _paymentService.ProcessPayment(order.Id);
await _inventoryService.UpdateStock(order.Items);
await _emailService.SendConfirmation(order.CustomerEmail);
_logger.LogInformation(...);
return Ok(...);
}
// 改造后:3行
[HttpPost]
public async Task<ActionResult<OrderResult>> CreateOrder([FromBody] CreateOrderCommand command)
{
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetOrder), new { id = result.OrderId }, result);
}2. 可测试性提升
csharp
// 改造前:需要 Mock 5个服务
[Fact]
public async Task CreateOrder_Success()
{
var mockOrderService = new Mock<OrderService>();
var mockPaymentService = new Mock<PaymentService>();
var mockEmailService = new Mock<EmailService>();
var mockInventoryService = new Mock<InventoryService>();
var mockLogger = new Mock<ILogger>();
var controller = new OrdersController(
mockOrderService.Object,
mockPaymentService.Object,
mockEmailService.Object,
mockInventoryService.Object,
mockLogger.Object);
// 测试复杂...
}
// 改造后:只需测试 Handler
[Fact]
public async Task CreateOrderHandler_Success()
{
var mockRepo = new Mock<IOrderRepository>();
var handler = new CreateOrderHandler(mockRepo.Object, ...);
var result = await handler.Handle(new CreateOrderCommand { ... }, CancellationToken.None);
Assert.NotNull(result);
mockRepo.Verify(r => r.AddAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Once);
}3. 扩展性提升
csharp
// 需求:订单创建后增加发送短信通知
// 改造前:需要修改控制器
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] OrderRequest request)
{
await _smsService.SendSms(order.CustomerPhone); // 新增
return Ok(...);
}
// 改造后:只需新增处理器,无需修改现有代码
public class SendSmsHandler : INotificationHandler<OrderCreatedNotification>
{
public async Task Handle(OrderCreatedNotification notification, CancellationToken ct)
{
await _smsService.SendSmsAsync(notification.CustomerPhone);
}
}✅ 符合开闭原则:对扩展开放,对修改关闭
🎓 总结
改造步骤回顾
- ✅ 安装 MediatR NuGet 包
- ✅ 定义命令和查询
- ✅ 实现 Handler 迁移业务逻辑
- ✅ 简化控制器
- ✅ 添加管道行为(日志、验证)
- ✅ 使用通知解耦副作用
核心价值
✅ 解耦: 控制器不依赖具体服务
✅ 简洁: 控制器代码减少 80%
✅ 一致: 统一的请求处理模式
✅ 易测: Handler 易于单元测试
✅ 扩展: 符合开闭原则
💡 提示: 从小模块开始重构,逐步推广到整个应用!