Queopius

Controladores gordos em Laravel

Um controlador deve coordenar a entrada HTTP, não concentrar a validação, as regras de negócio, as consultas, a autorização, as transformações, o envio de e-mail e as decisões completas de fluxo.

Laravel Verificação de integridade5 minutos
Imagem de destaque do artigo Fat Controllers in Laravel publicado em LinkedIn por Queopius
Laravel Verificação de integridadeControladoresArquiteturaCapacidade de manutenção

O controlador não deve estar onde tudo vive

Em Laravel é muito fácil começar a resolver a lógica directamente no controlador. O pedido chega lá, a framework facilita a validação, consulta de modelos, retorno de JSON e disparo de processos. Para uma primeira versão, esta velocidade pode ser útil.

O problema aparece quando este padrão se torna a norma. O método store começa por validar, depois decide as permissões, depois consulta vários modelos, calcula totais, cria registos, envia notificações e constrói uma resposta manual. O controlador deixa de ser um gateway e passa a ser o caso de utilização completo.

Um controlador saudável coordena. Recebe o pedido, delega validação, autorização e execução e devolve uma resposta. Quando absorve todo o comportamento, a aplicação pode continuar a funcionar, mas cada endpoint torna-se mais difícil de ler, testar e reutilizar.

Sinais de um controlador muito grande

O sinal mais evidente são os métodos de armazenamento, atualização ou destruição de muitas linhas. Mas o tamanho não é o único indicador. É também de observar se existe validação manual dentro do método, consultas complexas, condições de negócio misturadas com HTTP, autorizações repetidas ou transformações de respostas manuscritas.

Outro sintoma comum é encontrar o envio de e-mails, jobs, notificações ou integrações a correr diretamente a partir do controlador. Por vezes, também aparecem try/catch genéricos que escondem erros reais, código duplicado entre endpoints ou respostas inconsistentes dependendo de quem implementou cada método.

Se para testar o comportamento de um negócio precisa de preparar um pedido completo, autenticar o utilizador, configurar muitas dependências e passar por tudo HTTP, provavelmente o caso de uso está muito ligado ao controlador.

Por que razão acontece tanta coisa?

O controlador é o primeiro ponto visível quando chega um pedido. Num projeto jovem, a solução parece natural: está à mão, é rapidamente compreendido e não requer decisão sobre uma estrutura. A pressão para entregar recursos reforça esta decisão porque criar uma Ação ou um Serviço parece um trabalho extra.

O problema é cumulativo. Um período isolado não faz mal. Dez regras distribuídas por vários controladores, sim. Quando não existe uma camada de aplicação clara, cada endpoint acaba por resolver a sua própria versão do processo. Isto gera duplicações, inconsistências e uma arquitetura que depende muito de HTTP.

Laravel não o obriga a escrever controladores gordos. Torna muito barato começar assim. Portanto, quando a aplicação já está a sustentar o negócio, vale a pena verificar se esta conveniência inicial está a prejudicar a manutenção.

Que riscos gera?

Um controlador grande torna os endpoints mais difíceis de manter. A alteração de uma regra pode afetar a validação, persistência, resposta, notificações e permissões no mesmo método. O custo cognitivo aumenta porque o leitor precisa de compreender muitas camadas ao mesmo tempo.

Também aparecem testes mais frágeis. Se o comportamento residir dentro do controlador, testá-lo sem passar por HTTP é complicado. Isto leva-o a escrever testes de características muito amplos para regras que poderiam ser testadas mais diretamente se estivessem numa ação ou serviço.

O acoplamento com HTTP dificulta a reutilização da lógica em comandos, tarefas, processos internos ou alternativas APIs. Uma pequena alteração pode ter um impacto inesperado porque a lógica não é isolada. A médio prazo, refatorar é mais assustador do que continuar a acumular código.

Como perder peso num controlador

A primeira extração é, normalmente, a validação. Os pedidos de formulário permitem remover as regras HTTP do método e tornar o controlador mais limpo. A autorização deve ser movida para Políticas ou Portões quando a decisão for repetida ou representar uma regra de acesso clara.

Os casos de utilização enquadram-se bem em Ações ou Serviços. O nome e a responsabilidade não importam tanto: uma classe que expressa “criar pedido”, “aprovar pedido” ou “sincronizar cliente” permite testar o fluxo sem depender de HTTP. As respostas podem ser delegadas nos recursos API para evitar transformações manuais dispersas.

Processos assíncronos, e-mails pesados ou integrações devem ir para Jobs quando faz sentido. Os eventos e os ouvintes podem ajudar a dissociar os efeitos secundários, mas não devem ser utilizados como esconderijo para a lógica. Os DTOs ou Objetos de Dados podem ser úteis se o projeto necessitar de passar dados entre camadas de forma mais clara.

Exemplo conceptual

Antes: um método store valida campos, verifica permissões, cria vários registos, calcula valores, aplica descontos, envia um e-mail, despoleta uma notificação, regista atividades e devolve uma resposta personalizada. Tudo funciona, mas o controlador já não coordena: executa todo o caso de utilização.

Depois: StoreOrderRequest valida. OrderPolicy autoriza. CreateOrderAction executa o caso de utilização. OrderResource transforma a saída. Um Job gere tarefas pesadas. O controlador está reduzido a algumas linhas que parecem uma sequência clara.

A vantagem é prática. Se a validação mudar, já sabe onde tocar. Se o caso de utilização mudar, tente sem HTTP. Se a resposta mudar, modifica o recurso. Se a remessa for alterada, revê o trabalho. Cada peça tem menos motivos para mudar.

Lista de verificação rápida

Um controlador merece revisão se contiver métodos com mais de 40 ou 50 linhas, regras de negócio, validações repetidas, consultas complexas, e-mails diretos ou notificações, transformações de respostas manuais ou lógica duplicada entre endpoints.

A pergunta útil não é “posso mover isto?”, mas “seria mais claro se esta decisão ocorresse fora de HTTP?” Se puder testar o caso de utilização sem passar por um pedido completo, provavelmente será mais fácil manter a aplicação.

Fechando

Um controlador grande é geralmente um sinal precoce de que a aplicação está a perder a separação de responsabilidades. Nem sempre é urgente, mas é aconselhável detetá-lo antes que cada extremidade se torne uma peça frágil.

Se os seus controladores começam a parecer casos de utilização completos, uma auditoria de back-end Laravel pode ajudá-lo a detetar qual a lógica que deve ser extraída primeiro e quais as alterações que reduziriam mais riscos.

O seu Laravel apresenta sinais semelhantes?

Se a sua aplicação funcionar, mas cada alteração começar a ficar mais lenta, mais frágil ou mais difícil de explicar, uma auditoria de back-end Laravel pode ajudá-lo a resolver riscos, ganhos rápidos e prioridades técnicas.