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