DI containers, when used wrong, i.e., in most cases, are not just code polluters, but the absolute evil.
DI containers are very often called from all over the place. DI container in this case is just a service locator, which is a singleton. Singleton with huge amount of methods. Besides that it introduces hidden dependencies. That’s how it might look like:
class PaymentProcessor{public function purchase(){return DIPaymentModuleFactory::me()->purchase();}}
To remedy this mistake, the only way is to use it only on the highest level of an application, preferably in index file where an application starts. And if you’d use it that way — do you really still need it? You don’t anyway. It’s Catch-22 already, and there are two more points ahead.
DI container breaks the metaphor of a Lego brick that David West talks about in his book Object Thinking. I want my classes to be instantiated in a single place, and this place should be an entry point of any request or message. Their collaboration should be achieved only with composition. And composability is the goal that any aspiring object-thinker is striving for.
Moreover, it breaks fundamental OOP principles — data encapsulation and exposing behavior instead. Why the way an object should be instantiated is hidden to the program code? Why is it put in some configuration file? Really, think about it — it’s nonsense! This point is even broader. Why do we ever need a config file? Why is some piece of data torn apart of its behavior? This is exactly what Martin Fowler talked about 14 years ago, right? And everyone seems to agree with that. But why do we keep following this concept? Configuration files are just reflective of a procedural mindset!
It’s a pure Dependency Injection. So that’s how my entry point usually looks like:
try {echo (new PurchaseOrder(new LocalOrderStorage(new NullOrderStorage()),new OrderId($inputParams['order_id'])))->newInvoice(new InvoiceNumber(new Vendor(new LocalVendorStorage(),new VendorId($inputParams['vendor_id'])),new VendorInvoiceNumber($inputParams['vendor_invoice_number']),new DateTime($inputParams['date_time'])),new VendorInvoiceNumber($inputParams['vendor_invoice_number']),new DateTime($inputParams['date_time']),new InvoiceAmount(new Amount($inputParams['amount']),new Currency($inputParams['currency'])))->json();} catch (Exxeption $exxeption) {return (new ErrorResult())->json($exxeption->getCode(), $exxeption->getMessage());}
It’s wonderful. Join me.