Developers building Laravel applications are being urged to ditch the new keyword inside their controllers and switch to the Factory Method pattern. By moving object creation into dedicated factories, code becomes easier to extend, test, and maintain—benefits that matter as projects grow beyond a handful of endpoints.
Why the new keyword is a hidden liability
When a controller contains a line such as
$processor = new StripePaymentProcessor($config);
the controller is now tightly coupled to the StripePaymentProcessor class. Every place that needs a payment processor repeats that line, scattering the construction logic across the codebase. If the processor later requires a logger, a cache manager, or a different configuration format, each of those spots must be updated. The result is fragile code that resists change and is hard to mock in unit tests.
The Factory Method pattern steps in
The Factory Method pattern replaces direct instantiation with a method that returns an instance of a given interface. The controller depends on the interface, while a factory class knows which concrete class to build and how to assemble its dependencies. All creation logic lives in one place, so adding a new dependency or swapping implementations touches only the factory.
Core advantages
- Loose coupling – Controllers work against abstractions, not concrete classes.
- Centralized construction – Changing the build process requires editing a single file.
- Testability – Factories can be stubbed or replaced with mocks, allowing isolated controller tests.
- Open/Closed Principle – New features (e.g., a new payment gateway) can be added without touching existing controller code.
A coffee-shop analogy
Imagine a barista who must grind beans, steam milk, and pour water manually for every order, using a long series of if-else statements. If the latte recipe changes, every barista must relearn the steps. A coffee machine that receives an order type and handles the preparation internally solves the problem: the barista simply tells the machine what’s needed, and the machine encapsulates all the steps. Updating the recipe now means adjusting the machine, not every barista.
Laravel already leans on factories
Laravel’s own core components illustrate the pattern in action:
- Database –
ConnectionFactorydecides whether to build a MySQL or PostgreSQL connection. - Queues –
QueueManagercreates drivers for Redis, SQS, or other back-ends. - Filesystem –
FilesystemManagerproduces local or S3 disk instances. - Mail –
MailManagerresolves SMTP, Mailgun, or other mail drivers.
If the framework trusts factories for these critical services, custom code should follow suit.
When to introduce a factory
Use a factory when:
- Object creation involves multiple configuration steps or external services.
- Several interchangeable implementations exist (different payment gateways, storage providers, etc.).
- The list of possible implementations is expected to grow.
Avoid a factory when:
- Construction is a single, immutable
newcall with no extra setup. - Only one implementation will ever be used, making the extra abstraction unnecessary overhead.
Quick walk-through: refactoring a payment controller
- Define an interface –
PaymentProcessorInterfacewith aprocess(array $data)method. - Implement concrete classes –
StripePaymentProcessor,PayPalPaymentProcessor, each fulfilling the interface. - Create a factory –
PaymentProcessorFactorywith amake(string $driver): PaymentProcessorInterfacemethod. Inside, use aswitchor a map to return the appropriate class, injecting any required services from Laravel’s service container. - Inject the factory – In the controller’s constructor, type-hint
PaymentProcessorFactory. Laravel will resolve it automatically. - Use the factory –
$processor = $this->processorFactory->make('stripe'); $processor->process($request->all());
Now the controller never mentions new or a concrete processor. Adding a new gateway means creating a class and extending the factory’s map—no changes to the controller.
Potential downsides and how to mitigate them
A principal crítica às factories é a indireção adicional, que pode parecer verbosa para objetos triviais. O excesso de engenharia em um serviço simples pode inflar a base de código sem um benefício real. A chave é avaliar a complexidade da construção: se um objeto é um simples portador de dados sem dependências, um new direto pode ser aceitável. Além disso, as factories podem se tornar um depósito para lógicas não relacionadas. Manter as factories focadas na criação de objetos e delegar a configuração para service providers dedicados preserva a clareza.
O que observar a seguir
- Service container bindings – O container do Laravel pode resolver factories automaticamente se você vincular a interface à classe da factory em um service provider.
- Auto-discovery – Alguns pacotes expõem suas próprias factories; fique atento a colisões de nomes.
- Testing strategies – Ao realizar testes unitários de controllers, substitua a factory por um mock que retorne um processador stub, garantindo que os testes permaneçam rápidos e isolados.
Resumo
Substituir chamadas new espalhadas por uma factory bem posicionada centraliza a construção, reduz o acoplamento e alinha o código personalizado com a própria filosofia de design do Laravel. Para equipes que antecipam crescimento — novos provedores de pagamento, back-ends de armazenamento ou qualquer componente no estilo plug-in — o padrão Factory Method é um investimento de baixo custo que se paga em flexibilidade e confiança. Seu eu do futuro, e qualquer pessoa que herdar o código, agradecerá.
