Software · Arquitetura de Software
Vertical Slice Architecture: Organização por Funcionalidades em vez de Camadas Técnicas
Como a Vertical Slice Architecture maximiza a coesão do código ao agrupar controladores, regras de negócio e persistência em fatias funcionais autossuficientes.
Neste artigo
Em arquiteturas em camadas convencionais (como a clássica divisão em Apresentação, Aplicação, Domínio e Infraestrutura), o código é fatiado horizontalmente segundo critérios técnicos. Para implementar ou alterar uma funcionalidade simples (por exemplo, "Atualizar Endereço do Cliente"), o desenvolvedor é obrigado a percorrer e modificar arquivos espalhados em quatro ou cinco pastas diferentes do projeto, alterando controladores, serviços, interfaces abstratas, repositórios e esquemas de dados.
A Vertical Slice Architecture (Arquitetura em Fatias Verticais), popularizada por Jimmy Bogard, propõe inverter esse eixo de organização: em vez de agrupar o código por preocupações técnicas transversais, o sistema é fatiado verticalmente em torno de funcionalidades de negócio de ponta a ponta. Cada fatia (slice) encapsula tudo o que é estritamente necessário para atender a uma requisição específica.
A Mudança de Eixo: Fatiamento Horizontal versus Vertical
A transição entre esses paradigmas redefine a forma como as dependências se relacionam dentro do repositório:
flowchart TD
subgraph Horizontal["ARQUITETURA EM CAMADAS (HORIZONTAL)"]
direction TB
C["Camada de Controladores (Controllers)"]
S["Camada de Serviços de Aplicação (Services)"]
D["Camada de Domínio e Entidades (Domain)"]
R["Camada de Acesso a Dados (Repositories / Infra)"]
C --> S --> D --> R
end
subgraph Vertical["VERTICAL SLICE ARCHITECTURE (VERTICAL)"]
direction LR
subgraph Slice1["Fatia: CriarPedido"]
direction TB
E1["Endpoint"] --> V1["Validação"] --> H1["Lógica de Negócio"] --> P1["Persistência"]
end
subgraph Slice2["Fatia: CancelarPedido"]
direction TB
E2["Endpoint"] --> V2["Validação"] --> H2["Lógica de Negócio"] --> P2["Persistência"]
end
subgraph Slice3["Fatia: ObterRelatorio"]
direction TB
E3["Endpoint"] --> Q3["Consulta SQL Direta"] --> D3["Projeção DTO"]
end
end
No modelo horizontal, o acoplamento é sistêmico: alterar a assinatura de um método na camada de serviço pode quebrar três outros casos de uso que reutilizavam a mesma classe genérica. Na arquitetura vertical, cada fatia possui autonomia operacional; se a regra de validação do comando CriarPedido mudar, apenas os arquivos daquela pasta específica são editados, com risco nulo de efeitos colaterais sobre CancelarPedido ou ObterRelatorio.
Coesão Espacial e Pragmatismo Técnico
A maior vantagem da Vertical Slice Architecture é permitir que cada funcionalidade adote o nível exato de sofisticação exigido pelo seu problema, sem impor abstrações globais desnecessárias:
- Casos de Uso Simples (CRUD): Se uma consulta precisa apenas ler registros de uma tabela e devolver um JSON paginado na tela, a fatia pode executar uma consulta SQL direta otimizada via Dapper ou Entity Framework com
AsNoTracking(), sem necessidade de instanciar agregados ricos de DDD, interfaces de repositório e mapeadores intermediários. - Casos de Uso Complexos: Se a funcionalidade envolve transferências financeiras ou regras rigorosas de concorrência, a respectiva fatia pode modelar agregados completos, objetos de valor e eventos de domínio internamente, limitando a complexidade conceitual ao escopo onde ela realmente agrega valor.
// Exemplo de fatia vertical coesa para o caso de uso 'AprovarOrcamento'
namespace MeuApp.Features.Orcamentos.AprovarOrcamento;
// 1. Contrato da Requisição (Comando)
public record AprovarOrcamentoCommand(Guid OrcamentoId, string ResponsavelAprovacao) : IRequest<IResult>;
// 2. Validador específico da funcionalidade com FluentValidation
public class AprovarOrcamentoValidator : AbstractValidator<AprovarOrcamentoCommand>
{
public AprovarOrcamentoValidator()
{
RuleFor(x => x.OrcamentoId).NotEmpty();
RuleFor(x => x.ResponsavelAprovacao).NotEmpty().MaximumLength(100);
}
}
// 3. Manipulador que executa as regras e persiste o resultado
public class AprovarOrcamentoHandler : IRequestHandler<AprovarOrcamentoCommand, IResult>
{
private readonly AppDbContext _db;
public AprovarOrcamentoHandler(AppDbContext db) => _db = db;
public async Task<IResult> Handle(AprovarOrcamentoCommand cmd, CancellationToken ct)
{
var orcamento = await _db.Orcamentos.FindAsync(new object[] { cmd.OrcamentoId }, ct);
if (orcamento == null) return Results.NotFound("Orçamento não localizado.");
orcamento.Aprovar(cmd.ResponsavelAprovacao);
await _db.SaveChangesAsync(ct);
return Results.Ok(new { Mensagem = "Orçamento aprovado com sucesso.", Id = orcamento.Id });
}
}
// 4. Mapeamento de Endpoint HTTP na mesma fatia
public static class Endpoint
{
public static void Map(IEndpointRouteBuilder app) =>
app.MapPost("/api/orcamentos/{id}/aprovar", async (Guid id, IMediator mediator) =>
await mediator.Send(new AprovarOrcamentoCommand(id, "UsuarioAutenticado")));
}
Nesse arranjo, toda a cadeia da requisição (do recebimento HTTP à persistência no banco) reside em um único arquivo ou em uma pasta dedicada Features/Orcamentos/AprovarOrcamento/, reduzindo drasticamente a carga cognitiva necessária para compreender e manter o código.
Comparativo: Vertical Slice versus Clean Architecture
Embora ambas compartilhem o objetivo de manter as regras de negócio desacopladas de ferramentas externas, a estratégia de divisão interna diverge substancialmente:
| Critério | Clean Architecture | Vertical Slice Architecture |
|---|---|---|
| Organização de Pastas | Por círculos de abstração técnica (Domain, Application, Infrastructure). |
Por pastas de funcionalidades (Features/Usuarios/CriarUsuario). |
| Reaproveitamento de Classes | Alto reuso de repositórios e serviços genéricos compartilhados. | Baixo acoplamento entre casos de uso; cada fatia é independente. |
| Flexibilidade de Design | Rígida; todo caso de uso é forçado a transitar pelas 4 camadas. | Dinâmica; cada fatia escolhe entre consulta direta ou modelo rico. |
| Facilidade de Refatoração | Exige alterar múltiplos projetos e camadas para uma única tela. | Alterações ficam confinadas ao diretório da funcionalidade. |
Diretrizes Práticas para Produção
Na arquitetura de sistemas escaláveis, a Vertical Slice Architecture costuma ser adotada em combinação com o padrão REPR (Request-Endpoint-Response) e Minimal APIs:
- Evitar o "Monólito de Fatias": Compartilhar apenas infraestrutura genérica essencial (conexão de banco de dados, middleware de logging estruturado e configurações de autenticação), resistindo à tentação de criar serviços auxiliares compartilhados que voltem a acoplar as fatias.
- Duplicação Sadia versus Acoplamento Tóxico: Se duas fatias precisam de um DTO ligeiramente parecido, prefira duplicar as três propriedades na nova fatia em vez de criar uma classe base abstrata compartilhada que impeça a evolução independente dos casos de uso.
- Ponto de Partida para Microsserviços: Quando uma funcionalidade atinge um volume extremo de acessos, a extração de uma fatia vertical para um microsserviço independente requer apenas recortar aquela pasta e seu endpoint, tornando a evolução física do sistema simples e previsível.