← How complex can a constructor be?
docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards
en/articles/from-tg/architecture/constructor.md
new file +56
−0
How complex can a constructor be?
Once, during the php-pro course, while I was talking about SOLID, one of the students asked:
"How many dependencies in a constructor can be considered normal? Three? Four? Is five already bad?"
Good question. But the answer isn't really about "numbers".
There's a popular opinion that a constructor should be as simple as possible, because "otherwise it's a sign of an SRP violation".
I disagree. The mere fact of having many dependencies isn't necessarily bad: if your object has a clear responsibility but needs 6–8 dependencies to fulfill it, that's fine. The problem is when they don't fit together logically or the object tries to do too much.
But if the object coordinates a complex yet single business action, many dependencies are normal and even right.
class OrderFinalizer
{
public function __construct(
private OrderRepository $orders,
private InventoryService $inventory,
private DiscountCalculator $discounts,
private TaxService $tax,
private PaymentGateway $payments,
private LoggerInterface $logger,
private EventDispatcherInterface $events,
) {}
}
Yes, there are 7 dependencies here, and all of them serve a single action — finalizing an order.
This is SRP in its purest form: a complex operation, but one responsibility.
If we start "smearing" this across factories or passing in 1 general-purpose service like AppContext, that's when it gets worse.
Because instead of an explicit contract you get opaque magic.
It's unclear what the class really needs — only by reading the code can you see what gets pulled out of the context.
This makes harder:
- refactoring (because the class looks like a black hole);
- testing (you mock everything because it's unclear what's actually used);
- understanding (a newcomer can't read the dependencies from the signature).
Conclusion
Don't be afraid to pass many dependencies if they are all needed. Be afraid of hiding dependencies in "convenient" wrappers like
AppContext.
Code must be explicit. That is architecture.
First published in Ukrainian in the Telegram channel “Created to die” on June 19, 2025.