Notifications e Pipeline Behaviors
ResumoNotifications (publish/subscribe) permitem que múltiplos handlers reajam a um evento. Pipeline Behaviors envolvem o processamento de mensagens — ideais para validação transversal com FluentValidation.
1. Objetivos de Aprendizagem#
- Publicar notifications para múltiplos handlers.
- Criar um Pipeline Behavior de validação.
- Integrar FluentValidation ao pipeline do MediatR.
2. Pré-requisitos#
- Requests e commands com MediatR (tópico 44.3).
3. Conceito#
- Notification (
INotification): um evento publicado comIPublisher.Publish; N handlers podem reagir (ex.: "produto criado" → enviar e-mail + invalidar cache). - Pipeline Behavior (
IPipelineBehavior): um wrapper em torno de todos os handlers — como um middleware para mensagens. O porquê: aplicar validação, logging e transações num único lugar, sem repetir em cada handler.
O Pipeline Behavior envolve o handler, como um middleware para mensagens:
Uma Notification faz fan-out para múltiplos handlers independentes:
4. Mão na Massa#
4.1. Setup#
dotnet add Catalog.Application package FluentValidation
dotnet add Catalog.Application package FluentValidation.DependencyInjectionExtensions
Registro:
builder.Services.AddValidatorsFromAssembly(typeof(AssemblyMarker).Assembly);
builder.Services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(typeof(AssemblyMarker).Assembly);
cfg.AddOpenBehavior(typeof(ValidationBehavior<,>));
});
4.2. Implementação Passo a Passo#
- Notification + handlers:
public record ProdutoCriadoNotification(Guid Id, string Nome) : INotification;
internal sealed class EnviarEmailHandler : INotificationHandler<ProdutoCriadoNotification>
{
public Task Handle(ProdutoCriadoNotification n, CancellationToken ct)
{
// enviar e-mail (simplificado)
return Task.CompletedTask;
}
}
internal sealed class InvalidarCacheHandler : INotificationHandler<ProdutoCriadoNotification>
{
public Task Handle(ProdutoCriadoNotification n, CancellationToken ct)
{
// invalidar cache
return Task.CompletedTask;
}
}
Publicando no command handler: await _publisher.Publish(new ProdutoCriadoNotification(id, nome), ct);
- Validador FluentValidation:
public class CreateProdutoValidator : AbstractValidator<CreateProdutoCommand>
{
public CreateProdutoValidator()
{
RuleFor(x => x.Produto.Nome).NotEmpty().MaximumLength(60);
RuleFor(x => x.Produto.Preco).GreaterThan(0);
}
}
- Behavior de validação:
public sealed class ValidationBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
=> _validators = validators;
public async Task<TResponse> Handle(TRequest request,
RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
if (_validators.Any())
{
var context = new ValidationContext<TRequest>(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(r => r.Errors)
.Where(f => f is not null)
.ToList();
if (failures.Count != 0)
throw new ValidationException(failures);
}
return await next();
}
}
4.3. Executando#
Enviar um CreateProdutoCommand inválido dispara ValidationException no behavior, antes do handler — capturada pelo handler global (Módulo 19) como 422.
5. Exemplo Completo#
Fluxo: Send(command) → ValidationBehavior valida → handler executa e Publish(notification) → múltiplos handlers reagem em paralelo.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Centralizar validação num behavior | Repetir validação em cada handler |
| Notifications para efeitos colaterais desacoplados | Acoplar e-mail/cache dentro do command handler |
Mapear ValidationException no handler global | Deixar a exceção virar 500 |
Atençãohandlers de notification não devem lançar exceções que quebrem o fluxo principal se forem apenas efeitos colaterais. Considere isolá-los (try/catch, fila) para que uma falha de e-mail não derrube a criação do produto.
7. Segurança e Produção#
- Um behavior é o lugar ideal para autorização por caso de uso e logging/auditoria centralizados.
- Efeitos colaterais críticos devem ser resilientes (retry, outbox) em produção.
8. Exercícios#
- Fácil: adicione um
LoggingBehaviorque loga cada request. - Médio: valide o
UpdateProdutoCommandcom FluentValidation. - Desafio: implemente um padrão outbox para publicar notifications de forma confiável.
9. Resumo#
Notifications propagam eventos a múltiplos handlers; Pipeline Behaviors envolvem o processamento para aplicar validação (FluentValidation), logging e autorização de forma transversal, mantendo os handlers focados.
10. Próximos Passos#
Você concluiu o curso. Revise os módulos de segurança (05, 25, 26, 27, 28) antes de publicar uma API em produção.
11. Referências#
- Documentação — MediatR: Notifications e Behaviors
- Documentação — FluentValidation
- Microsoft Learn — Padrão Outbox