test.ical.ly | getting the web by the balls

Nov/12

28

Don’t pull your dependencies

At the Symfony Live Berlin 2012 last week I joined a discussion about handling dependencies in services. One person mentioned that he used to inject the whole dependecy injection container (DIC) to a service in some cases. The service itself requests one or more services from the injected container and use it then for internal stuff.

The service pulls his dependencies here. However dependencies should always be pushed and injected directly in the constructor or with a setter if possible.

by Frank Stelzer*

In this article I first explain why in some cases it is needed to pull dependencies in Symfony2 and later why you should avoid this in all other cases. I joined the great workshop “testable codeKore and Toby hold at the Symfony Live and I got motivated writing this article furthermore. So, thank you for that :)

Cases a class needs the DIC to be injected

When you have read the cookbook entry “How to work with Scopes” you might remember that there is described passing the DIC to a service when it needs internally some other services within a narrower scope. For Symfony2 it is the request instance in the most cases, as it is defined in a seperate scope for good reason.
If you do not read the cookbook entry yet, just let me quote its most important fact here:

If your service depends on a scoped service, the best solution is to put it in the same scope (or a narrower one). Usually, this means putting your new service in the request scope.
But this is not always possible (for instance, a twig extension must be in the container scope as the Twig environment needs it as a dependency). In these cases, you should pass the entire container into your service and retrieve your dependency from the container each time you need it to be sure you have the right instance.

In other words that means – especially for Symfony2:

Do never ever inject the complete DIC to a service when you do not need the request there!

Some examples:

  • When you need the logger, just inject the logger service from Symfony.
  • When you need the mailer, just inject the mailer service. It’s that easy!
  • When you are writing a twig extension and need the request, just inject the request….. Ah, stop! That won’t work. In this very special case this is not possible. As you can read in the cookbook entry, any twig extension has to be in the container scope and it is not allowed to define such service in the request scope. So the only possibility is to inject the DIC here.

So, I think you got the idea. When you are in the need of injecting the DIC and when not. Statements exist, which claim that one could solve the request injection with the help of proxy classes. However I never have seen code about that. When you know how to do it or if there is another blog post out there, please drop me some hint in the comments.

In the next section I explain why it is not a good idea pulling your dependencies in any other case.

Cases pulling dependencies should be avoided

Let’s imagine some simple class which needs a logger and mailer somewhere. The creation of the mailer message is directly put there for simplicitiy, please ignore it.

Those few lines look not very bad on the first view. Logging and mailer logic is de-coupled and their services are used. The class uses dependency injection -somehow- and creates no new instances on its own or even worse calls some static helpers (Logger::debug()…).

But, and please read the next sentence very carefully:
As the class uses the DIC you create implicit outgoing dependencies to all services and classes defined in the container!

There is no official metric for this implicit dependencies. Outgoing dependencies however are known as Efferent Coupling and its value is the count of all those dependencies. You should be aware of it!
Do not mix this metric up with the almost equal pronounced Afferent Coupling, which represents the count of all incoming dependencies of a class.

When you use a class which gets the DIC injected, you never know which dependencies are used internally without checking the code. It could use templating, translation, the session any other defined service or even nothing. You just do not know. If you want to provide a robust API for this class this is consequently a no-go.

Let’s write some test for making this problem more obvious. Actually this is no real test, I only want to demonstrate the usage problem of the class.

    public function testDoSomething()
    {
        $container = new Container();

        $puller = new DependencyPuller($container);
        $result = $puller->doSomething();

        $this->assertTrue($result);
    }

Regaring the API of the class this test should be fine and work. But ah …


It won’t as the created container does not know the needed logger service.

So, you have to define the logger service first in your test container and try it again.

Damn it, it won’t work either, as the mailer service is undefined in the test container too. Just another dependency the class did not supposed to have.

After modifying the test the second time and adding the definition of the mailer service the test will pass.

Yeah, we did it! But was it abvious how the test has to be written from be beginning? No! It is no big deal implementing this test on its own, but in the case the test is written by some other developer the one or the other WTF moment will certainly arise.  Using a new service in your class will break your test again as your injected container knows nothing about this third service. Running the risk of creating regressions is very high so.

This simple demonstration has shown that the class has more dependencies as it originally seemed or worse you don’t even know the exact dependencies.

Solution

The solution for this problem is quite easy: All dependencies which are pulled within the class have to be injected directly in the constructor (or provide some setter for them) and the dependency to the container has to be removed completely. Now for every usage all dependencies have to be pushed to the class. With the help of the DIC this is done within seconds. Seconds which later on could save minutes or hours when the project grows up in size.

Speaking about time, another developer told me this method might be too much time consuming especially during debugging as for example for every class the logger service or any other new created service has to be injected additionally if needed. But I think this no real argument as those changes do not waste that much time and could be done very easily.

One side note about the former mentioned Efferent Coupling:
After modifcation the class has certainly a higher Efferent Coupling value. Here it raises from 2 to 3. In the original class there existed only dependencies to the ContainerInterface and the Swift_Message class. The cleaned version points to LoggerInterface, Swift_Mailer and the Swift_Message. This change of the Efferent Coupling is ok though. It is better to raise this value a bit and having no implicit dependencies than otherwise. If you want to check up this metric on your own you may have a look at PHP Depend.

Do not hesitate to drop me some comments why and if you are in the need of pulling dependencies or to inject the DIC in your services furthermore. Though pushing dependencies should always be the way to go!

* Frank Stelzer (@frastel) has been working with PHP since 2001 and fell in love with Symfony during his university studies more than 6 years ago.
After his degree he started to work in the eSports industry and developed for a high load platform. Focusing on performance, clean code, unit testing or other aspects of code quality he loves to review and discuss about code.
For increasing his support in the Symfony world Frank joined the SensioLabs Deutschland Team in 2011 where he is working as a software architect.

Feel free to contact if you’re interested to write your own guest post!

· ·



  • http://twitter.com/sebwebdev Sebastian K.

    Nice article, Frank. Thanks raising the topic again, seems there’s still a need for awareness! I have been advocating a long time, that injecting the container is actually evil and in fact an anti-pattern. Since it introduces implicit dependencies, obfuscating the class API, as you also mention.
    And the container itself is somewhat like a superglobal state resp. uber factory class. You allow the developer to pull anything he wants in a given context. Which is likely to introduce regression.

  • lsmith77

    There are some more cases where you might need to inject the DIC. Basically any time you have a lot of dynamic logic involved in choosing the service you need. For example if you have different algorithms to compute shipping costs based on the order (address, customer type etc). Another example I recently came across was a highly configure interface for statistical computation. The user was allowed to choose the computation they wanted on the data set from 3 dozen options.

    Then again in those cases one could say: why even manage the dependencies in the DIC? Its not illegal to write “new SomeClass” inside a class.

    Semi related to this topic is this ticket aiming to add DIC level support for wrapping services into proxies:
    https://github.com/symfony/symfony/issues/5012

    This could also be used to handle the case of the request service, since the service would be lazy initialized.

  • http://twitter.com/tPl0ch Thomas Ploch

    Getting the Request object inside the TwigExtension can also be easily achieved by listening to the kernel.request event. But still a very good article though :-)

  • http://twitter.com/sebwebdev Sebastian K.

    @lsmith77:disqus Without knowing the implementation details of the examples, my feeling is that I would want to encapsulate those dynamics/logic into own service resp. factory classes. It might be a valid option to inject the container there, especially with lazy loading in place.
    Which is really a great idea, when you have lots of executions paths (if-else, switch) and you don’t know which objects/service you will end up calling (and you want to avoid loading one heavy container upfront).

    Really looking forward to see the lazy loading option being released!

    Agreed that it’s not per se evil do write “new SomeClass”. But one has to be aware that this is a implicit, tightly coupled dependency, that can’t be mocked. It really depends on the layer you are working in. What might be a bad idea in the action layer, might be legitimate in others.

  • frastel

    @twitter-31899909:disqus
    Thanks :)

    @twitter-85050169:disqus
    We have this small discussion about twig extension yesterday on twitter. Thank you for this edited comment.

    It is really important to know that storing requests in a service should be avoided as this will cause problems during sub requests. The cookbook entry about scopes gives a good example about this case.

    @lsmith77:disqus
    Like Sebastian already mentioned some code examples or usage examples will be good for talking about this problem furthermore.

    However if i understood you correctly your problem with the bunch of dependencies could be solved with custom compiler passes and tagged services which could be added then during processing and parsing the definitions. Splitting up those dependencies into several small grouped sub classes will maybe help too.

    The shipping cost problem would i solve with voters or maybe listeners. Each voter or listener has its own algorithm and could decide if the given order belongs to it.

    About creating classes within a service:
    When you are in the need you to that you should at least inject the class name as argument. Then you have the possibility to assign some special test classes there.

  • lsmith77

    Obviously a factory should be used in the cases I mentioned, but even the factory will need to build the services. Either via “new SomeClass” or by using the DIC. As for compiler passes, this is not possible if the configuration of what service needs to be used is provided via a dynamic data source.

  • Pingback: Lessons Learned @ NZZ – Teil 4 – Symfony2 Controller | DaRaFF's Blog

  • Pingback: PHP Digest: Simplified Password Hashing, Creation of PHP Framework Using Symfony2 Components, Xdebug Tutorial and Much More | Zfort Group Blog

  • http://twitter.com/pqr7 pqr7

    Newbie question: If I push $logger and $mailer into constructor – it does mean that $logged and $mailed are instantiated objects already. Then, let us imagine, that doSomething() method only sends mails on weekends:
    if (curdate() === $weekend) { /* send mail via $mailed */}
    On working day my code creates $mailer object but do not used it actually.
    In opposite to “push dependencies”, while I am using “pull dependencies” – it also means lazy service objects creation which is good for performance reason.

    What would you recomend me: how to create srevice objects lazy and use “push dependency” pattern at the same time? (the question is not related to DI container of Symfony 2, could give me examples just in general not going deep into Symfony 2).

  • frastel

    @twitter-14154868:disqus Lazy loading is not possible in the context of dependency injection. Your classes needs every time all the requested dependencies no matter if the classes uses their dependencies or not.
    So on the first view this is not efficient.
    Here the second view :) : Pushing the dependencies in this case will produce instable code – in the worst case. During working days your code will run successfully but at the weekend it will break because the mailer initialization causes some errors or maybe the mailer service is just not (well) defined.
    The API for this class is not very clean too. You may use this class with a defined mailer or without, the same applies for unit testing.
    This behavior should be avoided in any case.

    Talking about performance another aspect is good to know:
    The DIC only creates a service until it is needed. If your service is never used during a request it will never be created. Complete services are “lazy loaded” in this way and not only some class.
    Let’s take a contact form for example, everything is done in the same controller action. On a GET request this form is displayed and during the POST request it will be validated and the form data is send via an email. The latter step is contained in a service. The POST request calls this service and it is constructed there. Service construction is not executed during the GET request. This is the more important aspect speaking about performance.

  • Mieke

<<

>>

Theme Design by devolux.nh2.me