Symfony2: Top Down Perspective of Service Configuration

One issue many people have with Dependency Injection is the way you can no longer jump to an implementation of a dependency in an IDE and see what it actually does or make changes to it. This is indeed a side effect of moving towards much more loosely coupled code. If you are injecting a dependency and type hinting it with an interface, then you cannot jump straight to the particular implementation without looking at the DI configuration to see what implementation is being used.

This is not a bad thing though in my opinion, it is just part of moving away from coding to an implementation instead coding to an interface. The code you are working on does not know about the details of the implementation only its public methods, so in many ways it helps to, in effect, only know that yourself when working on that class. A big advantage of Dependency Injection is shifting the decision of what implementation to use out of the code and into a separate configuration, which is  why it is a good thing to be ignorant of this configuration when coding.

The inverse way of looking at this issue is that if you are not using Dependency Injection to pull all of this configuration out of the code then you cannot get a top down perspective of how the application is wired together without digging through the code itself. By separating the configuration from the classes themselves  you can more easily get an overview of how all the classes work together in an application.  Once you have made this shift in your way of thinking then it seems less problematic to not be able to jump to the implementation when working in the code.

If you are working with Symfony2 then as well as just looking at the services config files there are some great tools to get an overview of the services.

Out of the box there is a console command that will print out a list of all the services in the service container.

php app/console container:debug
[container] Public services
Name                                            Scope     Class Name
acme.demo.listener                              container Acme\DemoBundle\ControllerListener
annotation_reader                               container Doctrine\Common\Annotations\FileCacheReader
assetic.asset_manager                           container Assetic\Factory\LazyAssetManager
assetic.controller                              prototype Symfony\Bundle\AsseticBundle\Controller\AsseticController
assetic.filter.yui_css                          container Assetic\Filter\Yui\CssCompressorFilter
assetic.filter_manager                          container Symfony\Bundle\AsseticBundle\FilterManager
assetic.request_listener                        container Symfony\Bundle\AsseticBundle\EventListener\RequestListener
cache_warmer                                    container Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate
data_collector.request                          container Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector
database_connection                             n/a       alias for doctrine.dbal.default_connection
debug.event_dispatcher                          n/a       alias for event_dispatcher
doctrine                                        container Symfony\Bundle\DoctrineBundle\Registry
doctrine.dbal.connection_factory                container Symfony\Bundle\DoctrineBundle\ConnectionFactory
doctrine.dbal.default_connection                container Doctrine\DBAL\Connection
doctrine.odm.mongodb.cache                      n/a       alias for doctrine.odm.mongodb.cache.array
doctrine.odm.mongodb.cache.array                container Doctrine\Common\Cache\ArrayCache
doctrine.odm.mongodb.default_configuration      container Doctrine\ODM\MongoDB\Configuration

You can also specify a service by id to get more detailed information:

php app/console list assetic.asset_manager
[container] Information for service assetic.asset_manager

Service Id   assetic.asset_manager
Class        Assetic\Factory\LazyAssetManager
Tags         -
Scope        container
Public       yes

Whilst the console command is certainly helpful, especially for resolving service ids to actual class names, there is more information it would be helpful to be able to view. Johannes M. Schmitt's Debugging Bundle provides more useful tools for working with services in Symfony2. Once installed it adds a new section to the web profiler called service container where you can view information about the services being used in your application.

There is an overall graph of the services, this is split into pages since a typical service graph will include a lot of services:

Service Graph

You can also view the services as a list. The list shows what calls were made to include services including where they originated from, the type of injection which took place, e.g. during construction and whether it was constructed for this call or already existed:

Service List

Most helpfully if you are trying to find out more about a particular service, you can search by service name with an auto-complete facility:

Service Id Autocomplete

You can then view the outgoing services for the chosen service, that is the services that are injected into that service and their dependencies:

Outgoing Service Graph

You can also choose to view the incoming services, that is the services that the chosen service is injected into.

Incoming Service

This all helps to visualise the use of the various services and how they interact with each other. This is a much more difficult task when the dependencies are not injected into classes. You may be able to see what dependencies a class has just from the code but you cannot see which other classes use those dependencies as well nor can you easily see what code uses the class you are working in.