DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

Abordagem Vertical Slice (VSA) em MediatR

Introdução

Existem algumas formas de estruturar o MediatR. Geralmente a estrutura utilziada é chamada de Estrutura Horizontal e é bem utiliza em projetos de pequeno porte. Acontece que abordagem horizontal ficaria insustentável rapidamente, pois a pasta Commands e Queries ficaria com centenas de arquivos.

Estrutura Horizontal

Projeto.Application

├── Commands
│   ├── Remessa
│   │   ├── CreateRemessaCommand.cs
│   │   └── CreateRemessaCommandHandler.cs
│   ├── Usuario <--- Crescendo
│   │   ├── CreateUserCommand.cs
│   │   └── ChangePasswordCommand.cs

├── Queries
│   ├── Remessa
│   │   └── GetRemessaByIdQuery.cs
│   ├── Usuario <--- Crescendo
│   │   └── GetUserByEmailQuery.cs
..
Enter fullscreen mode Exit fullscreen mode

A Melhor Abordagem: Vertical Slice Architecture (VSA)

O VSA com CQRS/MediatR prioriza a organização do código pro funcionalidade (feature/casos de usos) em vez de por tipo de objeto.

Então, em vez de ter comandos de várias entidades misturados na mesma pasta, você agrupa tudo que se refere aquela funcionalidade específica no mesmo lugar.

O trabalho do VSA é inverter isso, criando pastas para cada feature principal do sistema, e dentro dela, agrupa os Commands, Queries e DTOS relacionados.

Projeto.Application

├── Features  // Ou 'UseCases', 'Slices', etc.
│   
│   ├── Remessas  // Feature: Gerenciamento de Arquivos de Remessa
│   │   ├── CreateRemessa
│   │   │   ├── CreateRemessaCommand.cs
│   │   │   └── CreateRemessaCommandHandler.cs
│   │   ├── GetRemessaById
│   │   │   ├── GetRemessaByIdQuery.cs
│   │   │   └── GetRemessaByIdQueryHandler.cs
│   │   └── RemessaDTO.cs // DTOs que são específicos da feature Remessa
│   
│   ├── Usuarios // Feature: Gerenciamento de Usuários e Contas
│   │   ├── CreateUser
│   │   │   ├── CreateUserCommand.cs
│   │   │   └── CreateUserCommandHandler.cs
│   │   ├── GetUserDetails
│   │   │   ├── GetUserDetailsQuery.cs
│   │   │   └── GetUserDetailsQueryHandler.cs
│   │   └── UserDTO.cs 
│   
│   └── Contas // Feature: Gerenciamento de Contas Bancárias
│       ├── CreateContaBancaria
│       │   ├── CreateContaBancariaCommand.cs
│       │   └── CreateContaBancariaCommandHandler.cs
..
Enter fullscreen mode Exit fullscreen mode

Desse modo, a gente consegue concluir que existe algumas vantagens:

  • Alta coesão e baixo acoplamento
  • Escalabilidade e Onborading (fácil para o desenvolvedor entender o sistema)
  • Isolamento de Dependências (DI)
  • Otimização do MediatR

Dica extra: Handle na mesma pasta de Command

Muitas vezes, o Command e o Handler são colocados no mesmo arquivo, reduzindo a poluição de arquivos, mas mantendo a estrutura de pastas clara. Exemplo:

// Projeto.Application/Features/Remessas/CreateRemessa/CreateRemessa.cs

public class CreateRemessaCommand : IRequest<RemessaDTO>
{
    // Propriedades do Command
}

public class CreateRemessaCommandHandler : IRequestHandler<CreateRemessaCommand, RemessaDTO>
{
    private readonly IRemessaRepository _remessaRepository;
    // ...
    public async Task<RemessaDTO> Handle(CreateRemessaCommand request, CancellationToken cancellationToken)
    {
        // Lógica de domínio/infraestrutura
    }

Enter fullscreen mode Exit fullscreen mode

Top comments (0)