Desacoplando chamadas com Mediator
Tags: netcore, designpatterns, mediator, command
Published: 2020-02-11 01:15:20

---
title: Desacoplando chamadas com Mediator
published: true
description: Como realizar chamadas de maneira simples e deixar seu código mais organizado.
tags: .net core, design patterns, mediator, command
---

Quando precisamos fazer chamadas em pontos da aplicação e não temos uma boa definição para isso, podemos acabar criando dependências que não façam sentido e deixar nosso código acoplado. Neste artigo iremos ver como trabalhar com o pattern Mediator e Command a fim de minimizar este problema e ter um design mais limpo. 

Se você estiver pensando algo do tipo: "Ei, mas eu já consigo desacoplar meu código com interfaces". Sim, é comum (e recomendável) utilizar IoC através de interfaces a fim de ter mais flexibilidade.
Mas te pergunto: Suas interfaces mudam? Com que frequência?

![all the time](https://dev-to-uploads.s3.amazonaws.com/i/ybg5az14zhmlgqzyofmh.gif)

Intefaces são muito importantes em linguagens fortemente tipadas, mas não devemos usá-las como a única solução para todos nossos problemas. Elas não foram feitas para serem alteradas com frequência, seu objetivo é definir um contrato para que qualquer classe que queira implementá-la tenha a mesma assinatura, nos permitindo assim substituir seu comportamento de maneira fácil onde ela é requisitada. 

**Qualquer alteração em uma interface levará a um refactor considerável em N partes do código.** 

Porém em certos pontos da nossa aplicação devemos ter mais flexibilidade. Tome como exemplo a camada mais externa de um sistema, seu objetivo é expor os dados para o client. Inevitavelmente vamos ter que mudar parâmetros, adicionar novos campos e fazer alterações baseadas em novas necessidades. Interfaces acabam sendo um inconveniente neste cenário. 

O pattern Mediator pode ser uma boa alternativa neste caso. Ele foi catalogado no conhecido livro "Design Patterns: Elements of Reusable Object-Oriented Software, GOF". Ele é, literalmente, um mediador entre duas classes que não se conhecem, nos permitindo realizar chamadas entre dois pontos sem criar acoplamento. Essas chamadas podem ser feitas através de um Command - outro pattern que teve a mesma origem. O Command trata de encapsular uma chamada através de uma classe que será enviada para execução.

Um pacote bem conhecido que implementa este pattern é o [MediatR](https://github.com/jbogard/MediatR). Iremos utilizá-lo neste exemplo.

##SHOW ME THE CODE!

Nosso projeto irá conter quatro csprojs, um web chamado API e três classlib chamados Application, Domain e Persistence. Todo o código exemplificado aqui está no meu GitHub.

{% github guisfits/decoupling-calls-with-mediatr no-readme %}

Tendo o [dotnet cli](https://dotnet.microsoft.com/download) instalado em sua máquina, vamos adicionar o MediatR ao nosso projeto e logo em seguida o pacote para configurá-lo.

`dotnet add API package MediatR`
`dotnet add API package MediatR.Extensions.Microsoft.DependencyInjection`

Vamos habilitar o MediatR através da classe startup na nossa API. Iremos primeiro carregar o assemply onde os handlers estaram implementados e passa-lo para o método _AddMediatR_.

> Repare que boa parte do código original foi omitido a fim de simplificar este exemplo. Também irei omitir processos não relacionados a este artigo, para mais detalhes consulte o repositório.

```csharp
using MediatR;

namespace API
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            var applicationAssembly = AppDomain.CurrentDomain.Load("Application");
            services.AddMediatR(applicationAssembly);
        }
    }
}

```

Na camada _Application_ também temos que adicionar o pacote _MediatR_ a fim de utilizarmos as interfaces _IRequest_ e _IRequestHandler_. 

> Um esclarecimento. O MediatR chama de Request o ato de disparar uma classe para processamento. Não o confunda com HttpRequest. 

`dotnet add Application package MediatR`

A interface IRequest tem como objetivo marcar nosso _Command_ para ser processado por um _Handler_. Também podemos utilizar o overload com generics de _IRequest<>_, onde passamos T como tipo de retorno que o Handler nos dará deste Command. 

Dentro de _Application_ iremos criar um comando para adicionar um novo usuário.

```csharp
using MediatR;

namespace Application.Commands
{
    public class CreateUserCommand : IRequest
    {
        public string Name { get; set; }
    }
}
```

Na camada _Domain_ iremos adicionar nossa entidade _User_ e seu repository, _IUsers_.


```csharp
public class User
{
    public User(string name)
    {
        Name = name;
    }

    public int Id { get; private set; }
    public string Name { get; private set; }
}
```

```csharp
public interface IUsers
{
    Task Add(User newUser);
}
```

Feito isso iremos para a parte mais importante, o Handler!
Vamos voltar para _Application_ e criar uma classe chamada _UserCommandHandler_

```csharp
class UserCommandHandler : IRequestHandler<CreateUserCommand>
{
    private readonly IUsers _users;

    public UserCommandHandler(IUsers users)
    {
        _users = users;
    }

    public async Task<Unit> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        Validate(request);

        var user = new User(request.Name);
        await _users.Add(user);

        return Unit.Value;
    }

    private void Validate(CreateUserCommand command)
    {
        if (string.IsNullOrEmpty(command.Name))
            throw new ArgumentException(nameof(command.Name));
    }
}
```

## Vamos testar

Em _API_ vamos criar _UserController_, obter a interface _IMediator_ e enviar o command para execução.

```csharp
[ApiController]
[Route("api/users")]
public class UserController : ControllerBase
{
    private readonly IMediator _mediator;

    public UserController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateUserCommand body)
    {
        await _mediator.Send(body);
        return Created("", body);
    }
}

```

Agora vamos fazer uma requisição para a rota criada

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vuw7hrw3gp0smu0ol8ck.png)

Como podemos ver no print, nosso request chegou até o handler.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jp9fqsaz3mxc90tvri4y.png)

Agora confirmamos o retorno com sucesso.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i8b4oauto5hdva9qv144.png)

### Pontos de Atenção

- Repare que nosso _Handler_ é uma classe privada, não dando a chance de classes externas o chamarem;
- Como o Handler é uma classe comum do C#, podemos receber as dependências através de IoC;
- Seguindo o _Single Responsibility Principle_, seria uma boa prática ter somente um Command por Handler. Assim segmentamos melhor nossas dependências e podemos evoluir a aplicação de maneira isolada;
- Não precisamos dizer explicitamente qual Handler implementa qual Command. O MediatR é esperto o suficiente pra fazer isso automaticamente pra gente;

### Recomendações

- [CQRS](https://martinfowler.com/bliki/CQRS.html): MediatR acaba nos ajudando na separação entre Commands e Queries;
- [Event Driven](https://martinfowler.com/articles/201701-event-driven.html): MediatR tem outra interface chamada INotification para lançar eventos, pode ser uma boa **alternativa em casos simples**;
- [IPipelineBehavior](https://github.com/jbogard/MediatR/wiki/Behaviors): Farei outro artigo em breve sobre essa funcionalidade;

## Conclusão

Vimos neste artigo um simples exemplo de como utilizar o pattern Mediator e Command através do MediatR a fim de um design mais elegante entre as chamadas da nossa aplicação. 

Não se esqueça de procurar a documentação oficial para mais detalhes e fique atento à mudanças em futuras versões.

Caso tenha gostado não deixe de curtir, comentar e divulgar este post se possível. Também fique a vontade para me seguir por aqui, no GitHub e me adicionar no Linkedin. Este é o meu primeiro artigo técnico, pretendo escrever muitos outros daqui pra frente.

Espero que esta leitura tenha sido de alguma ajuda pra você.

Um grande e abraço e até a próxima.