Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - Camadas (Layered Architecture)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

A arquitetura em camadas organiza o código em fatias horizontais, cada uma com uma responsabilidade única, em que uma camada só conversa com a camada imediatamente inferior. É o estilo mais difundido na indústria, presente em aplicações corporativas de CRUD, em sistemas legados de longa vida e em boa parte dos frameworks web que separaram rotas, serviços e repositórios por padrão. Sua promessa é separação de responsabilidades com curva de aprendizado baixa, e sua armadilha recorrente é virar estrutura cerimonial, em que as camadas existem no nome e a lógica de negócio escorre para os lugares errados. Em produção, o estilo funciona bem quando cada camada tem razão de existir clara e verificável.

Conceito e Camadas Típicas

A configuração clássica tem quatro camadas: apresentação, responsável pelo protocolo e pela interação com o usuário; negócio, onde vivem as regras e os casos de uso; persistência, que traduz objetos em consultas; e banco de dados, que garante integridade e armazenamento. Vale distinguir camadas de tiers: camadas são divisões lógicas de código, e tiers são divisões físicas de implantação; um sistema pode ter quatro camadas em dois tiers. A organização pressupõe que cada nível abstrai o inferior: a camada de negócio não sabe se a apresentação é web ou batch, e a de persistência não sabe por que aquela consulta existe.

Papel de cada nível na divisão de trabalho:

  • Apresentação: decide tudo que é detalhe de transporte, como rota, código de status, serialização e autenticação do canal.
  • Negócio: decide o que o sistema faz com o pedido do usuário, aplicando regras que valem igualmente para web, fila ou lote.
  • Persistência: decide como objetos viram linhas e consultas, sem opinião sobre quando aquilo é correto do ponto de vista comercial.
  • Banco de dados: decide como o dado é guardado de forma íntegra, com restrições, índices e transações como última linha de defesa.

A força do estilo está na substituição previsível: trocar a interface gráfica, o protocolo de exposição ou o banco sem tocar nas regras, desde que as dependências apontem na direção correta. A fraqueza está no seu custo de cerimônia: cada nível extra adiciona mapeamento e chamada de passagem, e quando a lógica real é fina, o sistema inteiro vira correio de mensagens entre camadas.

Regra de Dependência e Fluxo de uma Requisição

A regra estrutural é única e inegociável: dependências apontam para baixo, e uma requisição desce camada por camada, transformando-se a cada nível, com a resposta subindo pelo mesmo caminho. Uma camada não pula níveis sem justificativa declarada e não conhece nada acima de si; a camada de negócio não pode receber um objeto HTTP, e a de persistência não pode devolver exceção com vocabulário de protocolo. É importante notar que a regra vale em tempo de compilação: o negócio referencia abstrações de persistência, e a implementação concreta é injetada em execução, o que é a base da testabilidade do estilo.

// Camada de apresentação: traduz HTTP para o contrato do negócio; sem regras
@RestController
class ControllerPedidos {
    // injeta a camada de negócio; o transporte é detalhe daqui para baixo
    RespostaPedido post(RequisicaoPedido req) {
        // valida formato, autenticação de transporte e mapeia para comando
    }
}

// Camada de negócio: onde vivem as regras; não conhece HTTP nem SQL
class ServicoPedidos {
    // decide aprovação, aplica desconto, garante limite e orquestra efeitos
    // chamada única para baixo: o repositório abstrai a persistência
}

// Camada de persistência: traduz objetos para consultas; sem regras de negócio
class RepositorioPedidos {
    // SELECT e INSERT com mapeamento; condições comerciais não pertencem aqui
}

Fluxo típico de uma requisição de ponta a ponta:

  1. Entrada na apresentação: o framework recebe HTTP, autentica o transporte e valida formato de campos.
  2. Tradução para comando: o controller converte DTO de entrada em comando do caso de uso, sem decidir nada de negócio.
  3. Execução no negócio: o serviço aplica regras, consulta estado via interfaces de repositório e decide o desfecho.
  4. Acesso à persistência: o repositório monta a consulta, executa no banco e reconstrói entidades.
  5. Volta com transformação: cada camada converte de volta, e a resposta chega ao cliente com vocabulário de protocolo.

Camadas Fechadas, Abertas e o Antipadrão Sinkhole

Camadas fechadas obrigam a requisição a passar por todas elas em ordem, o que protege decisões de alto nível de mudanças em detalhes baixos. Camadas abertas são atalhos declarados, úteis quando uma camada de utilidades compartilhadas não faz sentido para todo o fluxo; a abertura precisa ser explícita, documentada e rara, ou a pilha perde sentido. O equilíbrio é decisão arquitetural: cada nível fechado compra isolamento com chamadas de passagem.

O antipadrão sinkhole surge quando a maioria das requisições atravessa todas as camadas sem fazer nada: o controller só repassa, o serviço só repassa, o repositório executa um CRUD trivial. A métrica prática é a proporção de fluxos puramente de passagem; acima de oitenta por cento, a arquitetura está cobrando cerimônia sem entregar isolamento. As saídas honestas são duas: consolidar camadas quando a lógica é realmente fina, ou aceitar o custo consciente em troca da separação que ainda faz sentido, e registrar essa escolha para que ela não seja confundida com descuido.

Regras práticas para decidir fechamento e abertura:

  • Camada fechada por padrão: o fluxo passa por ela, e o atalho é exceção que precisa de motivo escrito.
  • Camada aberta só com escopo claro: utilidades de formatação podem ser abertas; regras de negócio, nunca.
  • Atalho permitido de cima para baixo, com registro: pular um nível é aceitável quando documentado, e nunca de baixo para cima.

Camadas Anêmicas e Vazamento de Lógica

O modelo anêmico é o sintoma clássico: entidades reduzidas a sacos de dados, com toda a lógica concentrada na camada de serviço. Com o tempo, o serviço vira um procedimento gigante, e as regras começam a escorrer para as bordas: validações comerciais no controller, condições de desconto dentro do SQL, regras de estado escondidas em triggers. O vazamento costuma aparecer quando entra o segundo canal de entrada, como um processamento em lote ou um consumidor de fila, e a regra que morava no controller precisa ser reescrita, porque nunca esteve onde deveria.

Consequências típicas do vazamento:

  • Duplicação de regras: o mesmo critério de crédito implementado no endpoint, no job e na consulta, divergindo em silêncio.
  • Testes frágeis: validar regra de negócio exige subir HTTP e banco, porque ela não existe isolada.
  • Custo de evolução: mudar uma política comercial exige caçar sua cópia em três camadas diferentes.
  • Repositório com opinião de negócio: consultas que decidem elegibilidade fazem a persistência virar segunda camada de regras.

Testabilidade e Estratégias de Verificação

O valor testável do estilo depende de as dependências serem invertidas nas bordas: a camada de negócio recebe interfaces de repositório, e não classes concretas, para que os testes substituam o nível inferior por dublês rápidos. Com isso, a maior parte das regras é verificada em testes de unidade milissegundo por caso, sem banco e sem HTTP. A camada de apresentação, fina por dever, recebe testes de contrato leves, e a persistência recebe testes de integração com banco efêmero, verificando mapeamento e consultas reais.

  • Unidade no negócio: serviços testados com repositórios falsos em memória, cobrindo regras e caminhos de erro.
  • Integração na persistência: consultas executadas contra banco real de teste, capturando divergência de mapeamento.
  • Contrato na apresentação: rotas verificadas quanto a status, formato e autenticação de transporte, sem lógica interna.
  • Cenário de ponta a ponta: poucos fluxos críticos cobertos de ponta a ponta em ambiente próximo ao de produção.

Se a injeção de dependência não existir e os serviços instanciarem repositórios diretamente, a testabilidade prometida não chega a existir; o estilo sem inversão nas bordas é apenas organização de pastas.

A proporção entre tipos de teste segue a lógica da arquitetura: como o negócio está isolado no meio, ele concentra a maioria dos testes de unidade, e as pontas recebem cobertura menor e mais específica. Quando o número de testes de ponta a ponta cresce muito, é sinal de que as regras não estão isoladas o suficiente, e o sistema está testando infraestrutura em vez de lógica.

Responsabilidades por Camada

Camada Responsabilidade legítima Não deve conter
Apresentação Protocolo, autenticação de transporte, mapeamento de DTO Regras de negócio, SQL, decisão de estado
Negócio Casos de uso, regras, orquestração de efeitos Detalhes de HTTP, de ORM e de filas
Persistência Consultas, mapeamento, transações técnicas Condições comerciais, cálculo de valores
Banco Integridade, índices, restrições, transações Lógica de aplicação escondida em triggers

A tabela funciona como checklist de revisão: quando um pull request traz condição comercial em SQL, ou cálculo de tarifa no controller, a fronteira vazou e a correção é mover a regra para o nível certo, não apenas discuti-la.

Camadas versus Arquiteturas Derivadas

Estilos como hexagonal, cebola e clean são, em essência, refinamentos do desenho em camadas que invertem a direção da dependência mais interna: em vez de o negócio depender da persistência, todos dependem de abstrações definidas pelo negócio. Para sistemas de CRUD dominante com lógica fina, camadas diretas bastam e cobram menos cerimônia. Para domínios ricos, com regras que precisam sobreviver a trocas de infraestrutura e a vários canais de entrada, a inversão de dependência paga o próprio custo, e o desenho em camadas evolui naturalmente para portas e adaptadores.

Contextos em que camadas diretas funcionam bem:

  • Sistemas de back office: cadastros, aprovações e relatórios com regras modestas e canais de entrada poucos.
  • Equipes em formação: a organização horizontal é didática, e a localização de qualquer código é previsível.
  • Produtos com prazo curto de validação: a cerimônia mínima acelera a entrega enquanto o domínio ainda está se descobrindo.

Erros Comuns e Boas Práticas

Sintoma em produção Causa provável Mitigação
Serviço com milhares de linhas e tudo dentro Camada de negócio como depósito Quebrar por casos de uso e entidades ricas
Mesma regra em controller, job e SQL Lógica vazada para as bordas Centralizar na camada de negócio e testar
Requisição atravessa tudo sem fazer nada Sinkhole por hábito, não por decisão Consolidar camadas ou registrar o custo aceito
Teste de regra exige banco e HTTP Dependências concretas nas bordas Interfaces nos repositórios e injeção
Exceção de SQL estourando no controller Erros de baixo nível atravessando níveis Tradução de exceções na fronteira da camada
Migrations conflitando entre equipes Schema único sem dono por área Convenção de prefixo por módulo e revisão cruzada
Consulta lenta escondida no serviço N+1 e JOINs criados sem critério Revisão de consultas na persistência com medição

Boas práticas que mantêm o estilo saudável:

  • Imponha a regra de dependência por teste: ferramentas de análise de dependência no pipeline valem mais que qualquer diagrama.
  • Mantenha a apresentação fina: se o controller decide algo que muda comportamento comercial, a fronteira foi violada.
  • Dê voz ativa à camada de negócio: entidades com comportamento e casos de uso explícitos evitam a deriva anêmica.
  • Registre camadas abertas e por quê: todo atalho declarado tem contexto; sem registro, o atalho vira convenção acidental.
  • Meça o sinkhole periodicamente: amostrar fluxos e classificar passagem versus lógica real orienta simplificação com dado.
  • Mantenha DTOs nas bordas, não no meio: objetos de transporte param na apresentação; entidades e comandos vivem no negócio.
  • Reveja a pilha a cada trimestre: camadas que não justificam mais o custo podem ser consolidadas sem drama, se a decisão for registrada.