Depois de trabalhar com dezenas de projetos Laravel — desde MVPs de 3 meses até sistemas com 5 anos de história e centenas de models — chegamos a uma conclusão: a estrutura padrão do Laravel é ótima para começar, mas precisa evoluir conforme o projeto cresce. Aqui está como fazemos isso.

O problema da estrutura padrão em escala

A estrutura de diretórios que o Laravel entrega por padrão é flat e funciona bem até ~30 models e ~20 controllers. Além disso, problemas começam a aparecer:

  • Controllers com 500+ linhas misturando lógica de negócio com HTTP
  • Models com 300+ linhas de scopes, accessors, mutators e relacionamentos
  • Lógica de negócio duplicada entre controllers e jobs
  • Testes que não sabem onde morar
  • Dificuldade de onboarding de novos desenvolvedores

A solução não é abandonar o Laravel — é adicionar camadas arquiteturais dentro dele.

Nossa estrutura de diretórios

Adotamos uma abordagem de módulos por domínio dentro do Laravel, sem pacotes externos:

  • app/Domain/ — lógica de negócio pura (sem dependência de Laravel)
  • app/Application/ — use cases, commands, queries (orquestram o domínio)
  • app/Infrastructure/ — implementações concretas (Eloquent, Redis, S3, APIs externas)
  • app/Http/ — Controllers, Requests, Resources (finos, apenas HTTP)
  • app/Console/ — Commands Artisan (apenas dispatcher, lógica vai nos use cases)

Dentro de cada pasta, organizamos por contexto de negócio (ex: Domain/Orders/, Domain/Billing/, Domain/Notifications/).

Padrões que adotamos

Actions (não Services)

Preferimos o padrão Action ao Service: cada classe faz exatamente uma coisa, com um método público execute() ou handle(). CreateOrderAction, ApplyDiscountAction, SendInvoiceEmailAction. É mais fácil de testar, mais fácil de encontrar e elimina o problema de Services que crescem para 2000 linhas.

Form Request Objects para validação

Toda validação vive em Form Requests, nunca no controller. Isso separa a regra de validação da lógica de processamento e permite reutilização entre HTTP e console.

API Resources para serialização

Nenhum JSON é construído no controller. Toda resposta de API passa por um Resource ou ResourceCollection, o que garante consistência e facilita versionamento.

Events + Listeners para side effects

Quando uma ação de negócio dispara efeitos colaterais (email, notificação, webhook), usamos Events. O Action dispara o evento, os Listeners fazem o trabalho. Isso mantém Actions focadas e os efeitos testáveis isoladamente.

Testing strategy

Organizamos os testes em três camadas:

  • Unit tests: para Domain e Application (sem banco de dados, rápidos)
  • Feature tests: para HTTP endpoints (fazem hit no banco via SQLite in-memory)
  • Integration tests: para integrações externas (APIs de pagamento, email, etc.) — rodam apenas em CI com serviços reais ou mocks explícitos

Meta: 80%+ de cobertura em Domain e Application, pelo menos um feature test por endpoint crítico.

Pipeline de CI/CD

Todo projeto segue o mesmo pipeline no GitHub Actions:

  • PHPStan nível 8 (análise estática) — falha bloqueia merge
  • Laravel Pint (formatação) — falha bloqueia merge
  • Suite de testes completa — falha bloqueia merge
  • Deploy automático para staging ao mergear em develop
  • Deploy para produção apenas com aprovação manual no GitHub Actions

Quando essa estrutura compensa

Honestidade: essa estrutura tem custo inicial. Para um MVP que vai durar 3 meses, pode ser over-engineering. Para um projeto que vai ser mantido por 2+ anos com uma equipe crescente, o ROI é claro: menos bugs, onboarding mais rápido, refatorações menos arriscadas.

A heurística que usamos: se o projeto tem probabilidade alta de escalar além de 50 models, adote a estrutura completa desde o início. É muito mais caro refatorar depois.

Conclusão

Laravel é suficientemente flexível para acomodar desde a aplicação mais simples até sistemas enterprise complexos. A chave é adicionar estrutura deliberadamente, conforme o projeto justifica — nem cedo demais (over-engineering) nem tarde demais (dívida técnica acumulada).

Se quiser ver como aplicaríamos essa estrutura no seu projeto específico, entre em contato.

VOLTAR AO BLOG