Mediator

Guia completo para implementar Clean Architecture com MediatR

Por que usar Mediator?

Pense em um grande restaurante. Sem Mediator, cada cliente precisa conhecer pessoalmente cada cozinheiro e ligar diretamente para ele: "João, faz uma pizza para mim". Com Mediator, há um garçom central (o Mediator) que recebe todos os pedidos e os entrega ao cozinheiro correto.

Vantagem principal

Se o cozinheiro João tirar férias, o cliente não precisa saber - o garçom simplesmente entrega o pedido ao cozinheiro Pedro.


Instalação do MediatR

Para instalar o MediatR, deve-se acessar o terminal na pasta do projeto Application e executar o seguinte comando:

dotnet add package MediatR

Como resultado, o arquivo Aviator.Application.csproj será modificado, sendo adicionada a seguinte referência:

<PackageReference Include="MediatR" Version="12.2.0" />

O MediatR é uma biblioteca que implementa o padrão Mediator para a plataforma .NET. Atua como um mecanismo de intermediação inteligente entre os emissores de comandos (Commands) e seus respectivos manipuladores (Handlers). Amplamente adotado pela comunidade .NET, o pacote é mantido por Jimmy Bogard.


Análise Arquivo por Arquivo

Cada arquivo de código-fonte será examinado individualmente, apresentando-se as alterações necessárias para a implementação do padrão Mediator.


Response.cs

Localização: Aviator.Application/Planes/UseCases/Create/Response.cs

Implementação Anterior (sem Mediator)

Implementação Atual (com Mediator)

Análise das Alterações

Modificação Realizada: A implementação da interface IResponse foi removida, transformando a classe em um record simples sem vínculos com interfaces específicas.

Motivação Técnica: O MediatR adota uma abordagem mais flexível em relação aos tipos de retorno, não exigindo que as classes de resposta implementem interfaces pré-definidas. Esta característica reduz a quantidade de código boilerplate (repetitivo) necessário para a estruturação do sistema.

Comparação Conceitual

Aspecto
Abordagem Anterior (sem Mediator)
Abordagem Atual (com Mediator)

Estrutura de Resposta

Exigia implementação de IResponse (formulário oficial)

Aceita qualquer tipo como resposta (formato livre)

Flexibilidade

Rígida - interface obrigatória

Flexível - sem interfaces específicas

Complexidade

Maior - código boilerplate adicional

Menor - estrutura simplificada

Requisitos

Conformidade com contrato de interface

Conteúdo informativo adequado

Benefício Principal

Redução da complexidade e eliminação de código redundante, mantendo a funcionalidade essencial do sistema.


Command.cs

Localização: Aviator.Application/Planes/UseCases/Create/Command.cs

Implementação Anterior (sem Mediator)

Implementação Atual (com Mediator)

Análise das Alterações

Modificação Principal

A interface ICommand foi substituída por IRequest<Response>, estabelecendo um vínculo explícito entre o comando e seu tipo de retorno.

Decodificação da Estrutura

  • IRequest: Define a natureza de solicitação/requisição

  • <Response>: Especifica o tipo de objeto retornado após o processamento

Tradução Funcional

A classe Command passa a representar formalmente: "Uma solicitação que, quando processada, produz uma resposta do tipo Response"

Comparação Conceitual

Aspecto
Abordagem Anterior
Abordagem Atual

Interface

ICommand (genérica)

IRequest<Response> (específica)

Definição

"Sou um pedido de comida"

"Sou um pedido de pizza que resultará em uma nota fiscal"

Vínculo

Sem associação explícita de retorno

Associação explícita com tipo de resposta

Especificidade

Genérica

Tipada com retorno definido

Consideração Importante

O tipo especificado entre os delimitadores angulares (<Response>) deve corresponder exatamente ao tipo retornado pelo Handler correspondente, garantindo a consistência de tipos em tempo de compilação.


Handler.cs

Localização: Aviator.Application/Planes/UseCases/Create/Handler.cs

Implementação Anterior (sem Mediator)

Implementação Atual (com Mediator)

Análise das Alterações

Mudança 1

  • IHandler<Command, Response>IRequestHandler<Command, Response> Justificativa: Padronização com MediatR, substituindo interface personalizada pela padrão da biblioteca.

Mudança 2

  • HandleAsync(Command command)Handle(Command request, CancellationToken)

Parâmetros atualizados

  • Command request solicitação a ser processada

  • CancellationToken mecanismo de "botão de emergência" que interrompe processamento quando cliente cancela requisição ou fecha conexão

Definição de CancellationToken

Mecanismo que atua como um "botão de emergência" em operações assíncronas, permitindo a interrupção do processamento quando o cliente cancela a requisição ou fecha a conexão.

Comparação de Segurança em Operações Assíncronas

Cenário
Implementação
Comportamento

Sem CancellationToken

await Task.Delay(30000);

Aguarda 30 segundos mesmo após cancelamento do cliente

Com CancellationToken

await Task.Delay(30000, cancellationToken);

Interrompe imediatamente ao detectar cancelamento

Alterações Adicionais

Alteração
Descrição
Impacto

Nome do Método

HandleAsyncHandle

Padronização com convenção MediatR

Parâmetro Nomeado

commandrequest

Convenção de nomenclatura (opcional)

Passagem de Token

Task.Delay(10)Task.Delay(10, cancellationToken)

Boa prática para operações assíncronas

Importância do CancellationToken

O CancellationToken representa uma melhoria significativa na resiliência do sistema, prevenindo o consumo desnecessário de recursos quando operações são abandonadas pelos clientes, contribuindo para a eficiência e estabilidade da aplicação.


DependencyInjection.cs (NOVO)

Localização: Aviator.Application/DependencyInjection.cs

Funcionalidade implementada

  1. O MediatR realiza a varredura do assembly Aviator.Application.

  2. Identifica automaticamente todas as classes que implementam IRequestHandler<TRequest, TResponse>.

  3. Efetua o registro dessas classes no container de injeção de dependência.

  4. Estabelece o mapeamento entre comandos (ou queries) e seus respectivos handlers.

Explicação do método de extensão

Um método de extensão consiste em uma funcionalidade que é adicionada a um tipo existente sem a necessidade de modificá-lo diretamente.

Exemplo de uso

  • Sem extensão: services.??? (não existe um método nativo AddApplication()).

  • Com extensão: services.AddApplication(); (o método passa a estar disponível para IServiceCollection).

Funcionamento do RegisterServicesFromAssembly

A operação pode ser compreendida por meio da seguinte analogia:

  • O MediatR atua como um detetive que entra em um prédio (o assembly referenciado por typeof(DependencyInjection).Assembly).

  • O detetive examina cada apartamento (cada classe definida no projeto).

  • Identifica os chefs (classes que implementam IRequestHandler<,>).

  • Registra suas especialidades (qual comando cada handler processa).

  • Constrói uma agenda de contatos (mapeamento interno utilizado pelo MediatR para rotear solicitações).

Definição de Assembly

Um assembly corresponde ao arquivo compilado (.dll) que contém o código intermediário (IL), metadados e recursos de um projeto.

A expressão typeof(DependencyInjection).Assembly retorna uma referência ao assembly no qual a classe DependencyInjection está definida — neste caso, o projeto Aviator.Application.


Program.cs (API)

Localização: Aviator.Api/Program.cs

Implementação Anterior (sem MediatR)

Implementação Atual (com MediatR)

Análise das Alterações

Aspecto
Implementação Anterior
Implementação Atual

Registro de Handlers

Manual (AddTransient<AviatorHandler>())

Automático (AddApplication())

Acoplamento do Endpoint

Direto ao Handler concreto

À abstração ISender

Invocação do Processamento

handler.HandleAsync(command)

sender.Send(command)

Conhecimento da API

Conhecia a implementação específica (AviatorHandler)

Conhece apenas a interface do mediador (ISender)

Explicação da Implementação

Mudança 1 (Registro Manual → Automático)

Na abordagem anterior, cada handler precisava ser registrado individualmente no container de DI, resultando em código repetitivo e propenso a erros de omissão. Com o MediatR e o método AddApplication(), todos os handlers são descobertos e registrados automaticamente através de scanning de assembly.

Mudança 2 (Handler Concreto → ISender)

O endpoint anterior estava fortemente acoplado a uma implementação específica (AviatorHandler), violando o princípio de Inversão de Dependência. Na nova abordagem, o endpoint depende apenas da abstração ISender do MediatR, que atua como um mediador responsável por rotear o comando para o handler apropriado.

Mudança 3 (HandleAsync() → Send())

A invocação direta do método do handler (HandleAsync) foi substituída pela chamada ao método Send() do mediador. Esta mudança encapsula a lógica de roteamento e execução, centralizando-a no MediatR.

Comparação Conceitual

Benefício Principal

Redução drástica do acoplamento entre a camada de API e a camada de aplicação. A API não precisa mais conhecer detalhes de implementação dos handlers, tornando o sistema mais modular, testável e fácil de manter. A adição de novos casos de uso não requer mais modificações no ponto de entrada da API.


Fluxo de Funcionamento do MediatR

Fluxo de Execução

Quando sender.Send(command) é chamado, nesta ordem:

  1. Endpoint: Invoca sender.Send(createPlaneCommand)

  2. MediatR: Identifica o tipo da requisição (CreatePlaneCommand)

  3. MediatR: Consulta seu mapeamento interno para localizar o handler correspondente

  4. Localização: Encontra que CreatePlaneHandler processa CreatePlaneCommand

  5. Resolução: Solicita ao container de DI uma instância de CreatePlaneHandler

  6. Execução: Invoca handler.Handle(createPlaneCommand, cancellationToken)

  7. Retorno: A Response é devolvida ao endpoint original


Mapeamento Interno (Visão Simplificada)


Exemplo de Mapeamentos Internos

  • CreatePlaneCommandCreatePlaneHandler

  • UpdatePlaneCommandUpdatePlaneHandler

  • GetPlaneQueryGetPlaneHandler


Registro Automático versus Manual

Registro Automático (com MediatR)

Registro Manual (sem MediatR)


Análise Comparativa

Benefício do Abordagem Automática

Eliminação de código repetitivo e propenso a erros. A adição de novos casos de uso não requer modificações na configuração de DI, tornando o sistema mais escalável e de mais fácil manutenção.

O MediatR atua como um service locator inteligente que gerencia automaticamente o roteamento entre comandos/queries e seus respectivos handlers.


Testando a Implementação

Execução da Aplicação

Saída esperada


Teste da Funcionalidade

  • Método: POST

  • URL: http://localhost:5070/v1/planes

  • Headers: Content-Type: application/json

  • Body:

Resposta esperada


Sequência do Fluxo da Requisição

  1. CLIENTE (Postman, cURL ou Frontend) Envia requisição HTTP POST com payload JSON

  2. ENDPOINT API (.NET MapPost) Recebe a requisição, desserializa o JSON para o objeto CreatePlaneCommand

  3. ISender.Send(createPlaneCommand) Invoca o mediador passando o comando para processamento

  4. MEDIATR (Roteamento interno) Identifica automaticamente qual Handler corresponde ao tipo CreatePlaneCommand

  5. HANDLER (CreatePlaneHandler) Executa a lógica de negócio contida no método Handle

  6. RESPONSE (Record de retorno) Cria o objeto de resposta (Response record)

  7. JSON (Serialização automática) Serializa o objeto Response para JSON e retorna ao cliente


Verificação dos Registros (Depuração)

Para confirmar o correto registro dos handlers no container de DI:

Observação

Esta verificação é recomendada apenas para ambientes de desenvolvimento, com o objetivo de validar a configuração do MediatR e o registro automático dos handlers.

Em produção, o MediatR gerencia esses mapeamentos internamente sem necessidade de intervenção manual.


Análise Crítica: Adoção do Padrão Mediator

Cenário Atual (3 Handlers)

Aspecto
Sem MediatR
Com MediatR
Impacto

Registro no DI

3 registros manuais (AddTransient<> para cada handler)

1 registro automático via AddApplication()

Redução: 3 linhas → 1 linha

Acoplamento

Endpoint conhece e depende diretamente do Handler específico

Endpoint conhece apenas a abstração ISender

Desacoplamento: API não sabe qual handler executa

Complexidade

Código simples e direto, mas com acoplamento forte

Introduz overhead do MediatR (biblioteca + roteamento interno)

Trade-off Simplicidade versus Flexibilidade

Manutenção

Fácil de entender para sistemas pequenos

Requer aprendizado do padrão MediatR

Curva de aprendizado inicial

Para 3 handlers, o benefício é moderado. Em sistemas pequenos e com pouca probabilidade de crescimento, a simplicidade da solução manual pode ser preferível.


Cenário Futuro (50 Handlers)

Aspecto
Sem MediatR
Com MediatR
Impacto

Registro no DI

50 registros manuais (código repetitivo e propenso a erros)

1 registro automático (configuração única)

Eficiência

Elimina código boilerplate

Acoplamento

Endpoints fortemente acoplados aos handlers específicos

Endpoints completamente desacoplados

Flexibilidade

Troca de handlers sem afetar APIs

Cross-cutting Concerns

Logging, validação, etc. repetidos em cada handler

Pipeline Behaviors

Implementação única para todos

Consistência

Comportamentos transversais centralizados

Manutenção

Mudar um handler pode exigir alteração em múltiplos endpoints

Endpoints não precisam ser modificados quando handlers mudam

Escalabilidade

Crescimento controlado do sistema

Novas Funcionalidades

Cada novo caso de uso exige modificação na configuração DI

Novos handlers são descobertos automaticamente

Produtividade

Desenvolvimento mais ágil

Para 50+ handlers, o benefício é enorme. O MediatR se torna essencial para gerenciar complexidade, promover desacoplamento e habilitar padrões arquiteturais avançados.


Análise de Custo-Benefício

Fator
Impacto em Sistema Pequeno
Impacto em Sistema Grande
Explicação

Complexidade Inicial

Alta (aprendizado do MediatR)

Baixa (custo diluído)

Curva de aprendizado se paga com crescimento

Código Boilerplate

Pouca redução

Redução drástica (50+ linhas eliminadas)

Benefício acumulativo com escala

Desacoplamento

Benefício limitado

Benefício crítico para manutenção

Sistemas grandes exigem modularidade

Cross-cutting

Pode ser excessivo

Essencial para consistência

Comportamentos transversais tornam-se necessários

Testabilidade

Melhora moderada

Melhora significativa

Handlers isolados são mais fáceis de testar

O MediatR oferece retorno sobre investimento crescente - quanto maior e mais complexo o sistema, maior o valor proporcionado pela padronização, desacoplamento e recursos como Pipeline Behaviors.


Diretrizes para Adoção do MediatR

Utilizar:

Contexto
Justificativa

Projeto com perspectiva de crescimento (> 10-20 handlers)

O custo inicial é diluído pelo benefício de escalabilidade e manutenção a longo prazo

Necessidade de funcionalidades avançadas (Pipeline Behaviors)

Implementação centralizada de logging, validação, caching, autorização e outras preocupações transversais

Sistema requer Pub/Sub (múltiplos handlers para um evento)

Suporte nativo a notificações e processamento paralelo de eventos

Equipe numerosa trabalhando em paralelo

Padronização que facilita a colaboração e reduz conflitos de integração

Sistema complexo com muitas cross-cutting concerns

Pipeline Behaviors eliminam duplicação de código em funcionalidades transversais

Aversão a dependências externas

Projetos que minimizam dependências de terceiros

Uso limitado ao roteamento básico

Se não aproveitará features avançadas, a solução manual pode ser mais adequada

Não utilizar:

Contexto
Justificativa

Projeto muito simples (3-5 handlers básicos)

Overhead da biblioteca não compensa o benefício em sistemas pequenos

Performance crítica extrema (microssegundos importam)

Roteamento interno do MediatR adiciona latência mensurável

Aversão a dependências externas

Projetos que minimizam dependências de terceiros

Uso limitado ao roteamento básico

Se não aproveitará features avançadas, a solução manual pode ser mais adequada


Análise de Decisão por Tamanho de Projeto

Tamanho do Projeto
Handlers
Recomendação
Razão Principal

Pequeno/Prova de Conceito

1-5

Avaliar necessidade

Overhead pode superar benefícios

Médio

6-15

Considerar uso

Começa a valer a pena para organização

Grande/Enterprise

16-50

Altamente recomendado

Benefícios de escalabilidade são críticos

Muito Grande

50+

Essencial

Gerenciamento manual se torna impraticável


Considerações Técnicas Específicas

Característica
Favorece MediatR
Desfavorece MediatR

Arquitetura

Clean Architecture, DDD, CQRS

Monolito simples, CRUD básico

Time

Múltiplos desenvolvedores, squads

Desenvolvedor único ou time muito pequeno

Manutenção

Longo prazo, evolução constante

Curto prazo, baixa manutenção

Testes

Testabilidade importante, TDD

Testes não são prioridade

Performance

Latência aceitável na casa de milissegundos

Micro-otimização crítica (microssegundos)

A decisão deve considerar não apenas o tamanho atual, mas a trajetória de crescimento do projeto.

MediatR oferece maior valor em sistemas que evoluirão em complexidade e exigirão padronização para sustentabilidade a longo prazo.


Transição para MediatR: Guia de Migração

Se o projeto foi iniciado sem MediatR e agora requer sua adoção, a transição é direta e segue um padrão consistente.

Alterações por Componente

Comando (Request)

Handler


Mudanças Principais

O que mudou
Antes
Depois

Contrato do Handler

Interface personalizada

Interface padrão do MediatR

Método de processamento

HandleAsync

Handle

Parâmetros

Apenas o comando

Comando + token de cancelamento

Tempo estimado por handler: 2-5 minutos


Etapas Adicionais da Migração

  1. Adicionar pacote MediatR ao projeto

  2. Configurar injeção de dependência

  3. Atualizar endpoints para usar ISender

  4. Remover registros manuais de handlers

A migração pode ser realizada de forma progressiva, permitindo convivência temporária entre handlers antigos e novos durante a transição.


Comparação Detalhada

Aspecto
Sem MediatR
Com MediatR

Acoplamento

Alto (Endpoint→Handler)

Baixo (Endpoint→ISender)

Registro

Manual (cada handler)

Automático (scanning)

Manutenção

Mais trabalho

Menos trabalho

Performance

Máxima

Pequeno overhead

Complexidade

Baixa

Média

Flexibilidade

Baixa

Alta

Cross-cutting

Em cada handler

Pipeline behaviors

Testabilidade

Igual

Igual


Próximos Passos (se usar MediatR)

Pipeline Behaviors (Middleware)


Validação Centralizada


Eventos (Pub/Sub)


Evoluindo a Implementação


Adicionando Validação com FluentValidation


Perguntas Frequentes

1. O MediatR substitui minha lógica de negócio?

Não! Apenas o roteamento Command→Handler. Sua lógica continua igual.

2. Posso migrar gradualmente?

Sim! Alguns handlers com MediatR, outros sem. O sistema funciona misturado.

3. E se dois Handlers processarem mesmo Command?

Erro em tempo de execução. MediatR não permite (um Command = um Handler).

4. Funciona com controllers MVC tradicionais?

Sim perfeitamente! Não é exclusivo para Minimal APIs.

5. O CancellationToken é obrigatório?

Não, mas recomendado. Use = default se não quiser lidar com cancelamento.


Resumo

Análise de Adoção do MediatR

Benefícios

Vantagem
Efeito Concreto

Desacoplamento alcançado

Separação estabelecida entre endpoints e implementações de handlers

Registro automatizado

Eliminada a necessidade de configuração manual de cada handler no container de DI

Pipeline integrado disponibilizado

Infraestrutura provida para implementação centralizada de logging, validação e outras preocupações transversais

Escalabilidade habilitada

Suporte estruturado implementado para crescimento progressivo do número de casos de uso

Convenções padronizadas adotadas

Alinhamento realizado com práticas consolidadas pela comunidade .NET

Contrapontos

Aspecto
Implicação

Dependência externa introduzida

Biblioteca de terceiros adicionada ao ecossistema do projeto

Overhead de performance adicionado

Latência incremental incorporada ao processo de roteamento

Curva de aprendizado requerida

Período de adaptação necessário para assimilação dos novos padrões

A adoção do MediatR é recomendada para sistemas que apresentam as seguintes características:

  • Volume significativo de casos de uso existentes ou planejados

  • Necessidade comprovada de separação clara entre camadas arquiteturais

  • Requisitos identificados para funcionalidades transversais centralizadas

Para protótipos ou sistemas de escopo reduzido, a complexidade introduzida pode ser desproporcional aos benefícios obtidos. Em contextos empresariais ou arquiteturas em evolução, o investimento é justificado pelos ganhos em manutenibilidade, capacidade de expansão e uniformidade da base de código.


Recomendações

Diretrizes para Decisão

Contexto
Recomendação
Critério Decisório

Novos Projetos

Inicie com MediatR se projetar >10 handlers ou necessitar de features avançadas

Análise prospectiva da complexidade arquitetural

Projetos Existentes

Considere migração ao ultrapassar 15 handlers com cross-cutting concerns repetitivos

Avaliação quantitativa e qualitativa da base atual

Princípio Orientador

O MediatR constitui uma ferramenta especializada para problemas específicos de mediação e desacoplamento. Sua adoção não é obrigatória, mas demonstra elevada utilidade quando aplicada ao contexto apropriado.

Indicador Prático

Se identificada a repetição de código transversal (ex: logging, validação) em múltiplos handlers, configura-se o momento adequado para avaliação do MediatR.


Implementação Realizada

Alterações Efetuadas

Benefícios Concretizados

  • Desacoplamento estabelecido entre endpoints e handlers

  • Infraestrutura preparada para pipeline behaviors

  • Base arquitetural escalável para crescimento futuro

Custos Inerentes

  • Introdução de dependência externa

  • Overhead mínimo de performance

  • Período necessário para aprendizado


Estratégia de Adoção Progressiva

Fase Inicial

Projetos de complexidade reduzida podem ser implementados sem MediatR.

Ponto de Transição

Migre para MediatR quando identificar:

  • Proliferação significativa de handlers (>15)

  • Duplicação de cross-cutting concerns

  • Necessidade de eventos de domínio

  • Requisito de desacoplamento forte entre camadas

Observação

A migração apresenta complexidade moderada (2-5 minutos por handler), com retorno justificado para sistemas em expansão arquitetural.

Atualizado