Vertical Slice Architecture: organizando por feature
1. Introdução à Vertical Slice Architecture
A Vertical Slice Architecture (Arquitetura de Fatias Verticais) surge como uma resposta direta aos problemas de manutenibilidade e escalabilidade que arquiteturas tradicionais em camadas (layered architecture) impõem em projetos de médio e grande porte. Seu princípio fundamental é simples, mas poderoso: organizar o código por features de negócio em vez de por camadas técnicas.
Em uma arquitetura em camadas típica (Controllers, Services, Repositories), uma única funcionalidade como "Cadastro de Usuário" espalha seu código por todas essas camadas. Quando o sistema cresce, qualquer alteração exige tocar em múltiplos pontos, aumentando o risco de regressão. A Vertical Slice inverte essa lógica: cada feature contém todo o código necessário para executar uma operação de negócio — desde a entrada (HTTP, fila, evento) até a persistência.
Diferentemente da Clean Architecture ou Onion Architecture, que também promovem separação por domínio, a Vertical Slice não impõe uma estrutura rígida de anéis ou camadas concêntricas. Em vez disso, cada slice é independente e autocontido, simplificando drasticamente a compreensão do fluxo.
2. Estrutura de um Slice: Componentes Internos
Um slice vertical representa uma feature completa. Vamos considerar um exemplo concreto: "Registrar Pedido". A estrutura de pastas poderia ser:
src/
features/
RegistrarPedido/
RegistrarPedidoCommand.cs
RegistrarPedidoHandler.cs
RegistrarPedidoValidator.cs
RegistrarPedidoController.cs
RegistrarPedidoRepository.cs
RegistrarPedidoMapper.cs
ConsultarPedido/
ConsultarPedidoQuery.cs
ConsultarPedidoHandler.cs
ConsultarPedidoController.cs
ConsultarPedidoRepository.cs
Cada pasta contém todos os artefatos daquela feature. O RegistrarPedidoHandler, por exemplo, encapsula:
- Validação do comando de entrada
- Lógica de negócio (calcular impostos, verificar estoque)
- Persistência (via repositório próprio do slice)
- Notificações (se aplicável)
Isso contrasta com a organização por camada:
src/
controllers/
PedidoController.cs (contém criar, consultar, cancelar)
services/
PedidoService.cs (contém toda lógica de pedidos)
repositories/
PedidoRepository.cs (contém todos os métodos CRUD)
Na Vertical Slice, se o requisito "Registrar Pedido" mudar, apenas os arquivos dentro da pasta RegistrarPedido/ são afetados.
3. Diferenças Cruciais para Arquiteturas em Camadas
A diferença essencial reside no acoplamento. Em MVC, MVVM ou MVP, as camadas são interdependentes: o Controller depende do Service, que depende do Repository. Qualquer mudança em uma camada propaga-se para as demais. Em projetos grandes, isso gera o temido "big ball of mud" — uma bola de lama onde modificar um ponto quebra outros imprevisivelmente.
A Vertical Slice quebra esse acoplamento porque cada slice é uma unidade autônoma. Um novo desenvolvedor pode entender a feature "Registrar Pedido" lendo apenas os 5-6 arquivos daquela pasta, sem precisar navegar por todo o sistema. Isso reduz drasticamente o tempo de onboarding e o custo de manutenção.
Além disso, a Vertical Slice facilita a remoção ou substituição de features. Se um módulo de "Pagamento por Boleto" for descontinuado, basta excluir a pasta correspondente — não há risco de deixar código morto em Services ou Repositories compartilhados.
4. Integração com Padrões de Projeto e Princípios SOLID
A Vertical Slice casa perfeitamente com o padrão Command/Query (CQRS) e o Mediator Pattern. Cada slice pode ser modelado como um comando (escrita) ou query (leitura), com um handler dedicado. O Mediator atua como orquestrador, mas não introduz acoplamento entre slices.
Exemplo de um handler típico:
public class RegistrarPedidoHandler
{
private readonly IRegistrarPedidoRepository _repository;
private readonly IRegistrarPedidoValidator _validator;
public RegistrarPedidoHandler(
IRegistrarPedidoRepository repository,
IRegistrarPedidoValidator validator)
{
_repository = repository;
_validator = validator;
}
public async Task<Resultado> Handle(RegistrarPedidoCommand command)
{
var validation = _validator.Validate(command);
if (!validation.IsValid)
return Resultado.Erro(validation.Erros);
var pedido = command.ToPedido();
await _repository.Salvar(pedido);
return Resultado.Sucesso(pedido.Id);
}
}
Observe a aplicação do Single Responsibility Principle (SRP) a nível de feature: cada handler tem uma única responsabilidade (processar aquela operação). A inversão de dependência é local: as interfaces IRegistrarPedidoRepository e IRegistrarPedidoValidator são definidas dentro do próprio slice, não em um namespace global. Isso evita dependências circulares e mantém o slice verdadeiramente independente.
5. Ciclo de Vida de uma Feature: Do Design à Implementação
O ciclo de vida de uma feature na Vertical Slice segue etapas claras:
- Identificação: A feature é definida como uma operação de negócio atômica (ex.: "Cancelar Pedido").
- Definição de contratos: Cria-se o comando/query e a interface do repositório (se necessário).
- Implementação do handler: Lógica de negócio, validação e persistência.
- Testabilidade: Cada slice é testável isoladamente. Testes unitários validam o handler com mocks do repositório; testes de integração validam a persistência real.
Exemplo de teste unitário para o handler:
[Fact]
public async Task DeveRegistrarPedidoComSucesso()
{
var command = new RegistrarPedidoCommand { /* dados */ };
var repoMock = new Mock<IRegistrarPedidoRepository>();
var validator = new RegistrarPedidoValidator();
var handler = new RegistrarPedidoHandler(repoMock.Object, validator);
var resultado = await handler.Handle(command);
Assert.True(resultado.Sucesso);
repoMock.Verify(r => r.Salvar(It.IsAny<Pedido>()), Times.Once);
}
Refatoração: para modificar uma feature, altera-se apenas os arquivos de seu slice. Se a mudança envolver lógica compartilhada (ex.: cálculo de frete), extrai-se para uma biblioteca comum, mas o slice continua responsável por orquestrar o uso dessa biblioteca.
6. Casos de Uso e Desafios Práticos
Quando adotar Vertical Slice:
- Projetos com múltiplas features independentes (ex.: e-commerce, ERP, SaaS)
- Equipes grandes trabalhando em paralelo (cada equipe assume um conjunto de slices)
- Sistemas legados que precisam ser gradualmente modularizados
Desafios comuns:
- Duplicação de código: Dois slices podem precisar de lógica similar (ex.: validação de CPF). A solução não é criar um Service global, mas sim extrair para uma biblioteca compartilhada (ex.: Infraestrutura.Validacao) que slices podem referenciar.
- Gerenciamento de dependências compartilhadas: Slices que usam o mesmo banco de dados ou fila precisam de interfaces de infraestrutura comuns. Cria-se módulos de infraestrutura (ex.: Infraestrutura.Persistencia) que expõem contratos, mas a implementação fica em cada slice ou em um módulo separado.
Estratégias de mitigação:
- Use pacotes NuGet internos ou módulos do projeto para código compartilhado.
- Mantenha os slices enxutos: se um slice cresce demais, divida-o em sub-features.
- Documente explicitamente as dependências entre slices (evite dependências implícitas).
7. Conclusão e Comparação Final com Temas Vizinhos
A Vertical Slice Architecture oferece vantagens claras: isolamento (cada feature é autocontida), paralelismo de desenvolvimento (equipes trabalham em slices diferentes sem conflito) e evolução independente (features podem ser modificadas ou removidas sem impacto global).
Quando evitar:
- Projetos muito pequenos (menos de 5 features) — a sobrecarga de estrutura pode não valer a pena.
- Features fortemente acopladas (ex.: sistema de contabilidade onde cada lançamento afeta múltiplos relatórios) — nesse caso, uma arquitetura orientada a eventos ou Clean Architecture pode ser mais adequada.
Relação com Onion e Clean Architecture: A Vertical Slice não substitui essas arquiteturas, mas as complementa. Você pode adotar Vertical Slice dentro de cada anel da Clean Architecture (ex.: cada slice tem seu próprio use case, entidade e repositório). Na prática, muitas equipes combinam ambas: usam Clean Architecture para organizar o domínio central e Vertical Slice para organizar as features de aplicação.
Em resumo, a Vertical Slice Architecture é uma abordagem pragmática e poderosa para manter a sanidade em projetos que crescem. Ela coloca o foco onde deve estar: nas features que entregam valor ao negócio.
Referências
- Vertical Slice Architecture - Jimmy Bogard — Artigo seminal do criador do AutoMapper, introduzindo o conceito e motivando sua adoção.
- Vertical Slice Architecture in .NET - Milan Jovanović — Guia prático com exemplos em C# e ASP.NET Core, mostrando implementação passo a passo.
- Vertical Slice vs. Clean Architecture - CodeOpinion — Comparação detalhada entre as duas abordagens, com prós e contras.
- Feature Slicing: A Practical Guide - Martin Fowler — Reflexão sobre como fatiar features de forma eficaz, base para a Vertical Slice.
- CQRS and Vertical Slices - Derek Comartin — Vídeo técnico demonstrando a integração de CQRS com Vertical Slice em um projeto real.
- Vertical Slice Architecture with MediatR - Khalid Abuhakmeh — Tutorial usando MediatR para implementar o padrão Mediator dentro dos slices.