Desenvolvendo uma API com Vertical Slice Architecture em ASP.NET Core

Ismael “Bill” Gasparin
#LocalizaLabs
Published in
4 min readApr 25, 2024
Representação da estrutura de diretórios e arquivos de uma API feita com Vertical Slice Architecture
Representação da estrutura de diretórios e arquivos de uma API feita com Vertical Slice Architecture

Desenvolver uma aplicação robusta e fácil de manter é um desafio constante para desenvolvedores. Uma arquitetura bem definida pode fazer toda a diferença nesse aspecto. Neste artigo, vamos explorar a Vertical Slice Architecture e como aplicá-la na construção de uma API em ASP.NET Core.

O que é Vertical Slice Architecture?

A Vertical Slice Architecture é um padrão de arquitetura de software que organiza o código em torno das funcionalidades do sistema, em vez de camadas técnicas. Em vez de dividir o código por camadas como apresentação, lógica de negócios e acesso a dados, a Vertical Slice propõe agrupar todo o código relacionado a uma funcionalidade específica em uma única unidade vertical.

Isso significa que cada funcionalidade da aplicação terá sua própria “fatia vertical” de código, contendo tudo o que é necessário para implementá-la, incluindo interfaces de usuário, lógica de negócios, acesso a dados e testes.

Principais Diferenciais em Comparação com MVC e Clean Architecture

A Vertical Slice Architecture difere das arquiteturas MVC e Clean Architecture em vários aspectos:

  • MVC (Model-View-Controller): Enquanto o MVC divide o código em três camadas (Model, View e Controller), a Vertical Slice agrupa o código por funcionalidade, tornando mais fácil manter uma visão clara e coesa da lógica relacionada a uma determinada feature.
  • Clean Architecture: A Clean Architecture define camadas (Entidades, Casos de Uso, Interfaces de Usuário, etc.), enquanto a Vertical Slice organiza o código em torno de funcionalidades específicas, o que pode simplificar a estrutura da aplicação e torná-la mais orientada ao domínio.

Benefícios da Vertical Slice Architecture

  • Clareza e coesão: O código relacionado a uma determinada funcionalidade está agrupado em um só lugar, facilitando sua compreensão e manutenção.
  • Desenvolvimento incremental: Permite o desenvolvimento de novas funcionalidades de forma incremental, sem afetar outras partes da aplicação.
  • Facilidade de testes: Os testes de unidade e integração são mais fáceis de escrever e manter, pois estão concentrados em uma única área funcional.
  • Desacoplamento: Cada fatia vertical é independente das outras, o que facilita a escalabilidade e a evolução da aplicação.

Pontos a considerar da Vertical Slice Architecture

  • Possível Duplicação de Lógica: Se não for bem implementada, pode levar à duplicação de lógica entre os diferentes slices.
  • Curva de Aprendizado: Pode ser um pouco desafiador para desenvolvedores menos experientes se acostumarem com essa abordagem, especialmente se estiverem mais familiarizados com o MVC tradicional.

Exemplo Prático: Construindo uma API de TODO List

Vamos criar uma API simples de TODO List usando ASP.NET Core, aplicando os princípios da Vertical Slice Architecture.

Link do exemplo no GitHub: https://github.com/ismaelgasparin/vertical-slice-todo-list

Estrutura do Projeto

/VerticalSliceTodoList
/Controllers
TodoController.cs
/Features
/TodoItems
/Commands
AddTodoItemCommand.cs
UpdateTodoItemCommand.cs
MarkAsTodoItemCommand.cs
DeleteTodoItemCommand.cs
/Queries
GetTodoListQuery.cs
GetTodoItemQuery.cs
/Domain
TodoItem.cs
/Infrastructure
TodoDbContext.cs

Implementação

Começaremos definindo nossa entidade TodoItem no domínio, que representará uma tarefa em nossa lista de TODOs.

TodoItem.cs (Domínio)

namespace VerticalSliceTodoList.Domain;

public class TodoItem
{
public Guid Id { get; private set; }
public string Title { get; private set; }
public bool IsCompleted { get; private set; }
// Outros membros e métodos omitidos para brevidade
}

Agora, vamos criar a estrutura para manipulação de TODOs na camada de features.

AddTodoItemCommand.cs (Comando)

using FluentResults;
using MediatR;

namespace VerticalSliceTodoList.Features.TodoItems.Commands;

public record AddTodoItemCommand : IRequest<Result<Guid>>
{
public string Title { get; set; } = string.Empty;
}

O comando AddTodoItemCommand representa a ação de adicionar uma nova tarefa à lista de TODOs.

AddTodoItemCommandHandler.cs (Handler do Comando)

using FluentResults;
using MediatR;
using VerticalSliceTodoList.Domain;
using VerticalSliceTodoList.Infrastructure;

namespace VerticalSliceTodoList.Features.TodoItems.Commands;

public class AddTodoItemCommandHandler : IRequestHandler<AddTodoItemCommand, Result<Guid>>
{
private readonly TodoDbContext _context;

public AddTodoItemCommandHandler(TodoDbContext context)
{
_context = context;
}

public async Task<Result<Guid>> Handle(AddTodoItemCommand request,
CancellationToken cancellationToken)
{
var todoItem = new TodoItem(request.Title);

await _context.TodoItems.AddAsync(todoItem, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);

return Result.Ok(todoItem.Id);
}
}

O handler do comando AddTodoItemCommand é responsável por executar a lógica de negócios para adicionar uma nova tarefa à lista de TODOs.

AddTodoItemCommandValidator.cs (Validador do Comando)

using FluentValidation;

namespace VerticalSliceTodoList.Features.TodoItems.Commands;

public class AddTodoItemCommandValidator : AbstractValidator<AddTodoItemCommand>
{
public AddTodoItemCommandValidator()
{
RuleFor(x => x.Title)
.NotEmpty()
.MinimumLength(3)
.MaximumLength(100);
}
}

O validador do comando AddTodoItemCommand é responsável por executar as validações (campos obrigatórios, tamanhos mínimos e máximos) antes de adicionar uma nova tarefa à lista de TODOs. Caso alguma validação falhe, o handler AddTodoItemCommandHandler não é executado.

TodoController.cs (Controlador)

using MediatR;
using Microsoft.AspNetCore.Mvc;
using VerticalSliceTodoList.Features.TodoItems.Commands;
using VerticalSliceTodoList.Features.TodoItems.Queries;
using VerticalSliceTodoList.Infrastructure.Extensions;

namespace VerticalSliceTodoList.Controllers;

[ApiController]
[Route("todo")]
[Produces("application/json")]
public class TodoController : ControllerBase
{
private readonly IMediator _mediator;

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

[HttpPost]
public async Task<IActionResult> AddTodoItem(
[FromBody] AddTodoItemCommand command,
CancellationToken cancellationToken = default)
{
var result = await _mediator.Send(command, cancellationToken);
return result.ToActionResult(
onSuccess: r =>
{
return new CreatedAtActionResult(
nameof(GetTodoItem), "todo", new { id = r.Value },
r.Value);
});
}
}

O controlador TodoController define os endpoints para manipulação de TODOs, neste caso, apenas o endpoint para adicionar uma nova tarefa.

Conclusão

A Vertical Slice Architecture é uma abordagem poderosa para organizar o código de uma aplicação, proporcionando clareza, coesão e facilidade de manutenção. Ao aplicar esse padrão em nossa API de TODO List, pudemos observar como ele nos permite desenvolver funcionalidades de forma incremental e desacoplada, facilitando o desenvolvimento e a evolução da aplicação.

No entanto, é importante estar ciente de que a Vertical Slice pode adicionar complexidade em projetos simples e pode exigir um esforço adicional de organização e planejamento. Portanto, ao decidir adotar essa arquitetura, é crucial avaliar cuidadosamente as necessidades do projeto e o contexto em que será aplicada.

Link no GitHub: https://github.com/ismaelgasparin/vertical-slice-todo-list

--

--