![Page 1: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/1.jpg)
deSymfonyDay Barcelona 2014
DIC To The Limit
![Page 2: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/2.jpg)
![Page 3: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/3.jpg)
Ronny López
ABOUT ME US
Hard way learner !Technical Lead at Social Point Do stuff at TangoTree in the nights !
@ronnyltwww.tangotree.iohttps://github.com/ronnylt
![Page 4: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/4.jpg)
AGENDA
• The problems
• Dependency Injection
• Coupling done right
• Dependency Inversion Principle
• Types of DI
• Dependency Injection Containers
• Symfony Dependency Injection Container
• Interchangeable Services
![Page 5: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/5.jpg)
THE PROBLEMS
• We require to switch out data access layer without compromising all the other parts of the application
• We need to use different implementations in different deployments (dragon game, monster game, etc…)
• We wish to deploy the application in different environments (testing, integration, staging, production, etc…)
• We need different configurations in each environment
![Page 6: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/6.jpg)
YAGNI You aren't gonna need it, let’s go home!
![Page 7: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/7.jpg)
IN CASE YOU NEED IT
• You have to do Dependency Injection, correctly
• You have to know and apply Dependency Inversion Principles
![Page 8: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/8.jpg)
WHAT WE REALLY WANT IS
• Code that is easy to test
• Clear separation of infrastructure logic from application logic
• Interchangeable infrastructure
![Page 9: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/9.jpg)
WHY?
• Multiple deployments share parts of the same code, but each deployment has specific infrastructure needs
• Multiple environments with different needs
• We want to automatically test all the things
• We want to go fast, and the only way to go fast is… you know…
![Page 10: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/10.jpg)
![Page 11: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/11.jpg)
“The only way to go fast, is to go
well”
Uncle Bob
![Page 12: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/12.jpg)
DEPENDENCY INJECTION
![Page 13: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/13.jpg)
Dependency injection is a simple way to decouple classes from what they
depend on
![Page 14: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/14.jpg)
Dependency injection decouples classes
construction from the construction of it’s
dependencies
![Page 15: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/15.jpg)
IS IT ALL ABOUT COUPLING?
![Page 16: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/16.jpg)
COUPLING
Mod A Mod B
Mod C Mod D
Tight (high, strong)
Mod A Mod B
Mod C Mod D
Loose (low, weak)
Mod A Mod B
Mod C Mod D
None
Coupling is a measure of the independency of components/modules
![Page 17: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/17.jpg)
COUPLING
• The history of software shows that coupling is bad, but it also suggest that coupling is unavoidable
• An absolutely decoupled application is useless because it adds no value
• Developers can only add value by coupling things together
![Page 18: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/18.jpg)
COUPLING DONE RIGHT
• Components make no assumptions about what other components do, but rely on their contracts
• Use an interface to define a type and focus on what is important
• Concrete implementations of the interfaces should be be instantiated outside the component
![Page 19: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/19.jpg)
DEPENDENCY INVERSION PRINCIPLE
![Page 20: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/20.jpg)
High-level modules should not depend on low-level
modules. Both should depend on
abstractions
![Page 21: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/21.jpg)
Abstractions should not depend on details.
Details should depend on abstractions
![Page 22: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/22.jpg)
DEPENDENCY INVERSION
• Decouple high level parts of the system from low level parts by using interfaces
![Page 23: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/23.jpg)
OrderProcessor OrderRepository
MySqlOrderRepository
CassandraOrderRepository
LockSystem
RedisLockSystem
ZooKeeperLockSystem
<<interface>>
<<interface>>
ObjectStore RedisStorageDriver
InMemoryStorageDriver
RiakStorageDriver
StorageDriver<<interface>>
![Page 24: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/24.jpg)
TYPES OF DEPENDENCY INJECTIONS
!
![Page 25: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/25.jpg)
TYPES OF DI
• Constructor Injection
• Setter Injection
• Interface Injection
• Property Injection
• Service Locator
![Page 26: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/26.jpg)
CONSTRUCTOR INJECTION
• Injects the dependencies via constructor
• It ensures that the choice of dependency is immutable
!class EnergyBuyCommandHandler implements CommandHandler!{! private $playerRepository;!! private $configRepository;!! public function __construct(!! ! PlayerRepository $playerRepository, !! ! ConfigRepository $config!! )! {! $this->playerRepository = $playerRepository;! $this->configRepository = $config;! }!}!
![Page 27: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/27.jpg)
CONSTRUCTOR INJECTION
• It ensures that the choice of dependency is immutable
• The constructor is only ever called once when the object is created, so you can be sure that the dependency will not change during the object's lifetime
Pros Cons
• It is not suitable for working with optional dependencies
• Difficult to use in combination with class hierarchies
![Page 28: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/28.jpg)
CONSTRUCTOR INJECTION
• Try to always use constructor injection
• If dealing with legacy code that does not support it, consider using an adapter class with constructor injection
• Depends on abstractions (interfaces), not concrete implementations
TIPS
![Page 29: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/29.jpg)
SETTER INJECTION
• Inject the dependencies via a setter method
• The “injector” has to call the method in order to inject the dependency
!class LoggerChain implements SQLLogger!{! private $loggers = array();!! public function setLogger(SQLLogger $logger)! {! $this->logger = $logger;! }!}!
![Page 30: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/30.jpg)
SETTER INJECTION
• Works “well” with optional dependenciesIf you do not need the dependency, then just do not call the setter
• You can call the setter multiple times.This is particularly useful if the method adds the dependency to a collection
Pros Cons
• Works “well” with optional dependencies Are you sure you need optional dependencies?
• You can call the setter multiple times
• You are not sure if the dependency was set
![Page 31: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/31.jpg)
SETTER INJECTIONTIPS
• Avoid setter injections (the choice of dependencies is not inmutable)
• If you do Dependency Inversion right, probably YANGI
• Remember, your classes depend on abstractions, not concrete implementations, so you can use Null or Dummy implementations when necessary
![Page 32: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/32.jpg)
SETTER INJECTIONEXAMPLE
use Psr\Log\LoggerInterface;!!final class OrderProcessor!{! private $logger;!! function __construct(. . ., LoggerInterface $logger)! {! $this->logger = $logger;! }!}!!final class GoodManLogger implements LoggerInterface {…}!!final class LogstarLogger implements LoggerInterface {…}!!final class NullLogger implements LoggerInterface {…}!
Instead of having a setter method to inject the logger, use constructor injection and use the appropriate logger implementation in each case
![Page 33: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/33.jpg)
INTERFACE INJECTION
• Define and use interfaces for the injection
• Allows certain objects to be injected into other objects, that implement a common interface
• It’s a kind of setter injection, so same pros and cons
interface ContainerAwareInterface!{! public function setContainer(ContainerInterface $container = null);!}!!class ContainerAwareEventDispatcher implements ContainerAwareInterface!{! public function setContainer(ContainerInterface $container = null)! {! }!}!
![Page 34: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/34.jpg)
PROPERTY INJECTION
• Allows setting public fields of the class directly
• There are mainly only disadvantages to using property injection, it is similar to setter injection but with additional important problems
!!class NewsletterManager!{! public $mailer;!! // ...!}!
![Page 35: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/35.jpg)
PROPERTY INJECTION
• Useful if you are working with code that is out of your control, such as in a 3rd party library, which uses public properties for its dependencies
Pros Cons
• You cannot control when the dependency is set at all, it can be changed at any point in the object's lifetime
• You cannot use type hinting so you cannot be sure what dependency is injected except by writing into the class code to explicitly test the class instance before using it
![Page 36: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/36.jpg)
SERVICES LOCATOR
• Is an object that knows how to get all of the services that an another service might need
interface CommandHandlerLocator !{! public function locate($commandName);!}!!!class ContainerCommandHandlerLocator implements CommandHandlerLocator!{! private $container;!! public function __construct(ContainerInterface $container)! {! $this->container = $container;! }!! public function locate($commandName)! {! if (!$this->container->has($commandName)) {! throw new NotFoundException('Unable to find command handler');! }!! return $this->container->get($commandName);! }!}!
![Page 37: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/37.jpg)
SERVICE LOCATOR
• It’s easy to use and abuse due to its straightforward behaviour
• Not all use cases are bad, for example when you want to load services on demand at runtime
Pros Cons
• It hides dependencies in your code making them difficult to figure out and potentially leads to errors that only manifest themselves at runtime
• It becomes unclear what are the dependencies of a given class
• It’s easy to abuse
• It’s consider and anti-pattern (but there are valid use cases for it)
![Page 38: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/38.jpg)
SERVICE LOCATORTIPS
• Use a segregated interface for the locator, do not depends on the whole locator (container)
!
• Limit the types of services a locator provides
![Page 39: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/39.jpg)
DEPENDENCY INJECTION CONTAINER
![Page 40: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/40.jpg)
Dependency Injection Container is simply an object that manages the
instantiation of other objects
![Page 41: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/41.jpg)
Automagically creates a given type with all the required dependencies
![Page 42: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/42.jpg)
DIC
• Most of the time you do not need a DIC to benefit from Dependency Injection
• But… creating and maintaining the dependencies by hand can become a nightmare pretty fast
• A DIC manages objects from their instantiation to their configuration
• The objects themselves should not know that they are managed by a container and should not know nothing about it
![Page 43: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/43.jpg)
DEPENDENCY INJECTION CONTAINERS IN PHP
![Page 44: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/44.jpg)
PHP DIC IMPLEMENTATIONS
• Twittee
• Pimple
• Illuminate\Di
• Zend\Di
• Symfony\DependencyInjection
• http://php-di.org/
• Build your own
![Page 45: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/45.jpg)
SYMFONY DEPENDENCY
INJECTION CONTAINER
![Page 46: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/46.jpg)
The Dependency Injection component allows you to standardize and centralize
the way objects are constructed in a SF
application
![Page 47: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/47.jpg)
HOW IT WORKS?
• Reads definition of how objects (services) should be constructed (XML, YAML, PHP, etc…)
• Collects all definitions and builds a container
• When requested, creates objects and injects the dependencies
![Page 48: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/48.jpg)
ADVANCED
HOW IT WORKS
• Compiler Passes
• Container Extensions
• Services Configurator
• Tagged Services
![Page 49: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/49.jpg)
SYMFONY DIC
• Basic (and advances) usages are well documented in the Symfony official documentation
• Probably you are comfortable creating your own objects via the container
• So, let’s try to solve the problems we stated at the beginning
![Page 50: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/50.jpg)
INTERCHANGEABLE SERVICES
![Page 51: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/51.jpg)
OrderProcessor OrderRepository
MySqlOrderRepository
CassandraOrderRepository
LockSystem
RedisLockSystem
ZooKeeperLockSystem
<<interface>>
<<interface>>
ObjectStore RedisStorageDriver
InMemoryStorageDriver
RiakStorageDriver
StorageDriver<<interface>>
![Page 52: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/52.jpg)
final class OrderProcessor!{! private $orderRepository;! private $lockSystem;! private $objectStore;!! function __construct(!! ! OrderRepository $repository, !! ! LockSystem $lockSystem, !! ! ObjectStore $objectStore)! {! $this->orderRepository = $repository;! $this->lockSystem = $lockSystem;! $this->objectStore = $objectStore;! }!}!!final class ObjectStore!{! function __construct(StorageDriver $driver)! {! }!}!!
!interface StorageDriver !{}!!final class RiakStorageDriver implements StorageDriver!{}!!final class RedisStorageDriver implements StorageDriver!{}!!final class InMemoryStorageDriver implements StorageDriver!{}!
!interface LockSystem !{}!!final class RedisLockSystem implements LockSystem!{}!!final class ZooKeeperLockSystem implements LockSystem!{}!
interface OrderRepository {}!!final class MySqlOrderRepository implements OrderRepository!{}!!final class CassandralOrderRepository implements OrderRepository!{}!
![Page 53: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/53.jpg)
services:!! order_processor:!! ! class: OrderProcessor! arguments:!! ! ! object_store: @object_store!! ! ! order_repository: [mysql|cassandra] !! ! ! lock_system: [redis|zookeeper]!! ! ! !!! object_store:!! ! class: ObjectStore!! ! arguments:!! ! storage_driver: [riak|redis|inmemory]!
MySqlOrderRepository
CassandraOrderRepository
RedisLockSystem
ZooKeeperLockSystem
RedisStorageDriver
InMemoryStorageDriver
RiakStorageDriver
![Page 54: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/54.jpg)
THE PROBLEM
• It’s not possible to decide in advance what concrete implementation a deployment or environment is going to use
• We must configure dependencies for all the concrete implementations and each one has their own dependencies
![Page 55: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/55.jpg)
SERVICES SETUP
Deployment Environment Concrete Implementation
DRAGON TESTING in-memory
DRAGON STAGING/QA redis
DRAGON PROD mysql
MONSTER TESTING in-memory
MONSTER PROD cassandra
![Page 56: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/56.jpg)
SOLUTIONS
1. Loading different configurations depending on the environment and aliasing services
2. Using synthetic services and aliases
3. Managing semantic configuration with extensions
4. Using a custom injector
![Page 57: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/57.jpg)
1- LOADING DIFFERENT CONFIGURATIONS
Load different configurations depending on the kernel.environment
• Probably easy to setup for testing/dummy services
Pros Cons
• The choice is coupled to the environment
• All services get defined, even when you are not going to use them
![Page 58: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/58.jpg)
!namespace Acme\DemoBundle\DependencyInjection;!!use Symfony\Component\DependencyInjection\ContainerBuilder;!use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;!use Symfony\Component\HttpKernel\DependencyInjection\Extension;!use Symfony\Component\Config\FileLocator;!!class AcmeDemoExtension extends Extension!{! public function load(array $configs, ContainerBuilder $container)! {! $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));! $loader->load('services.yml');!! if ($this->container->getParameter('kernel.environment') == 'test') {! $loader->load('services_test.yml');! }! }!! public function getAlias()! {! return 'acme_demo';! }!}!
services:! storage_driver:! alias: storage_driver.riak!! storage_driver.riak:! class: RiakStorageDriver!! storage_driver.redis:! class: RedisStorageDriver!! storage_driver.memory:! class: InMemoryStorageDriver
!services:! storage_driver:! alias: storage_driver.memory!
services.yml services_test.yml
The choice is coupled to the environment
![Page 59: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/59.jpg)
2- USING SYNTHETIC SERVICES AND ALIAS
Define abstraction as “synthetic” services
• Probably easy to setup for testing/dummy services
Pros Cons
• All services get defined, even when you are not going to use them
• You have to define dependencies of services you probably not are going to use
![Page 60: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/60.jpg)
!namespace Acme\DemoBundle\DependencyInjection;!!use Symfony\Component\DependencyInjection\ContainerBuilder;!use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;!use Symfony\Component\HttpKernel\DependencyInjection\Extension;!use Symfony\Component\Config\FileLocator;!!class AcmeDemoExtension extends Extension!{! public function load(array $configs, ContainerBuilder $container)! {! $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));! $loader->load('services.yml');! }!! public function getAlias()! {! return 'acme_demo';! }!}!
services:! storage_driver:! synthetic: true!! storage_driver.riak:! class: RiakStorageDriver!! storage_driver.redis:! class: RedisStorageDriver!! storage_driver.memory:! class: InMemoryStorageDriver!
!services:! storage_driver:! alias: storage_driver.redis!
services.yml app/config/config.yml
!services:! storage_driver:! alias: storage_driver.memory!
app/config/config_test.yml
![Page 61: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/61.jpg)
3- MANAGING SEMANTIC CONFIGURATIONS WITH EXTENSIONS
• Instead of having the user override individual parameters, you let the user configure just a few, specifically created options.
• As the bundle developer, you then parse through that configuration and load services inside an “Extension"
• With this method, you won't need to import any configuration resources from your main application configuration: the Extension class can handle all of this
![Page 62: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/62.jpg)
3- MANAGING SEMANTIC CONFIGURATIONS WITH EXTENSIONS
• Good fit if you are building a bundle to be used by 3rd parties and you have a lot of configuration options you want to validate
• Lot of boilerplate code
• Extra complexity added
• You just wanted to manage dependency injection right?
![Page 63: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/63.jpg)
class Configuration implements ConfigurationInterface!{! protected function addObjectStoreSection(ArrayNodeDefinition $rootNode)! {! $rootNode! ->children()! ->arrayNode('object_store')! ->isRequired()! ->children()! ->arrayNode('store_driver')! ->children()! ->scalarNode('type')->isRequired()->end()! ->scalarNode('connection')->end()! ->scalarNode('create_buckets')->end()! ->end()! ->validate()! ->ifTrue(function ($v) {! switch ($v['type']) {! case 'doctrine_dbal':! if (!isset($v['connection']) ||! !isset($v['create_buckets'])) {! return true;! }! break;! }!! return false;! })! ->thenInvalid('children configuration')! ->end()! ->isRequired()! ->end()! ->end()! ->end();! }!}
!$definition = $container->getDefinition($storageDriver);!switch ($config['storage_driver']['type']) {! case 'doctrine_dbal':! $definition->replaceArgument(0, new Reference($config['storage_driver']['connection']));! $definition->replaceArgument(1, $config['storage_driver']['create_buckets']);! break;! case 'redis':! $definition->replaceArgument(0, new Reference($config['storage_driver']['connection']));! break;!}!!$container->setAlias('object_storage.driver', $storageDriver);!
Configuration
Extension
![Page 64: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/64.jpg)
3- MANAGING SEMANTIC CONFIGURATIONS WITH EXTENSIONS
• More powerful than simply defining parameters: a specific option value might trigger the creation of many service definitions;
• Ability to have configuration hierarchy
• Smart merging of several config files (e.g. config_dev.yml and config.yml)
Pros Cons
• Too much verbose, a lot of boilerplate code
• Extra complexity added if you only want to define dependencies and not a long configuration tree
!
![Page 65: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/65.jpg)
4- USING A CUSTOM INJECTOR
• Let’s forget the Symfony DIC for a moment
• Let’s go back to dependency management problem again
![Page 66: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/66.jpg)
services:!! order_processor:!! ! class: OrderProcessor! arguments:!! ! ! object_store: @object_store!! ! ! order_repository: [mysql|cassandra] !! ! ! lock_system: [redis|zookeeper]!! ! ! !!! object_store:!! ! class: ObjectStore!! ! arguments:!! ! storage_driver: [riak|redis|inmemory]!
MySqlOrderRepository
CassandraOrderRepository
RedisLockSystem
ZooKeeperLockSystem
RedisStorageDriver
InMemoryStorageDriver
RiakStorageDriver
![Page 67: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/67.jpg)
“A good architecture allows you to defer critical decisions, it doesn’t force you to defer
them. However, if you can defer
them, it means you have lots of flexibility”
!
Uncle Bob
![Page 68: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/68.jpg)
“A good architecture allows volatile decisions to be easily
changed” !
Uncle Bob
![Page 69: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/69.jpg)
services:!! order_processor:!! ! class: OrderProcessor! arguments:!! ! ! object_store: @object_store!! ! ! order_repository: [mysql|cassandra] !! ! ! lock_system: [redis|zookeeper]!! ! ! !!! object_store:!! ! class: ObjectStore!! ! arguments:!! ! storage_driver: [riak|redis|inmemory]!
MySqlOrderRepository
CassandraOrderRepository
RedisLockSystem
ZooKeeperLockSystem
RedisStorageDriver
InMemoryStorageDriver
RiakStorageDriver
We need a dependency injection tool that allow us to easily change
volatile decisions
We need a dependency injection tool that allow us to defer critical
decisions
![Page 70: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/70.jpg)
Core Framework
Payment Component
Analytics Component
Object Store Lock System
Component Exports Depends On
Unit Of Work Storage Driver
Order Processor Product Repository
Order Repository
Payment Gateway
Tracker Tracker Queue
Implementations
Redis, Zookeper
Redis, C*, Riak, MySql
Config, API
MySql, C*
Itunes, Facebook, Amazon, Google
Play
Redis, RabbitMQ, SQS, Kinesis
![Page 71: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/71.jpg)
Core Component Object Store Lock System
Component Exports Depends On
Unit Of Work Storage Driver
Implementations
Redis, Zookeper
Redis, C*, Riak, MySql
namespace SP\Core\Game\Framework;!!class FrameworkComponent implements Component!{! public function getName()! {! return 'core.game.framework';! }!! public function getServicesDefinition()! {! return ServicesDefinition::create()!! ->dependsOn('storage_driver')! ->withInstanceOf('SP\Core\Component\ObjectStore\Storage\StorageDriver')!! ->dependsOn('lock_system')! ->withInstanceOf('SP\Core\Component\Lock\LockSystem')!! ->exports('object_store')! ->withClass('SP\Core\Component\ObjectStore')! ->andConstructorDependencies(‘storage_driver’, ‘lock_system’);! }!}
These are the decisions the component developer wants to defer
Depends on abstractions, not concrete implementations
![Page 72: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/72.jpg)
Payment Component Order Processor Product Repository
Order Repository
Config, API
MySql, C*
namespace SP\Core\Game\Payment;!!class PaymentComponent implements Component!{! public function getName()! {! return 'core.game.payment';! }!! public function getServicesDefinition()! {! return ServicesDefinition::create()!!! ! ! ->dependsOn('product_repository')! ->withInstanceOf('SP\Core\Game\Payment\Product\Repository\ProductRepository')!! ->dependsOn('order_repository')! ->withInstanceOf('SP\Core\Game\Payment\Order\Repository\OrderRepository')!! ->dependsOn('gateways')! ->withInstanceOf('GatewayDefinition')!! ->exports(‘order_processor')! ->withClass('SP\Core\Game\Payment\OrderProcessor')! ->andConstructorDependencies(‘gateways', ‘product_repository’, ‘order_repository’);! }!}
Itunes, Facebook, Amazon, Google
Play
Payment Gateway
These are the decisions the component developer wants to defer
Depends on abstractions, not concrete implementations
![Page 73: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/73.jpg)
WHERE DOES THE MAGIC COME FROM?
• There is not such “magic”
• Each component define it’s dependencies in a easy and legible way
• Framework agnostic dependency definition
• Based on the dependency definitions, services are added to the container during the container building phase
![Page 74: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/74.jpg)
HOW?
• First, collect services definitions from installed components
• Second, inject services definitions into the Symfony (or Silex, or…) container
• No magic, just code !!!
![Page 75: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/75.jpg)
USING SYMFONY
• Collect services definitions from installed components
• Inject services definitions into the Symfony (or Silex, or…) container
![Page 76: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/76.jpg)
use Symfony\Component\DependencyInjection as DI;!!!final class ComponentDependencyInjector implements DI\Compiler\CompilerPassInterface!{! private $components;!! public function __construct(array $components = array())! {! $this->components = $components;! }!! public function process(ContainerBuilder $container)! {! foreach ($this->components as $component) {! $services = $component->getServicesDefinition();! foreach ($services->dependencies as $definition) {! $id = $component->getName() . '.' . $definition->name;!! if (!$container->has($id)) {! throw new ServiceNotFoundException($component->getName(), $id, $definition->instanceOf);! }! }! }! }!! public function registerComponentsDependencies(ContainerBuilder $container)! {! foreach ($this->components as $component) {! $this->addComponentDependencies($container, $component);! }! }!! private function addComponentDependencies(ContainerBuilder $container, Component $component)! {! $container->addObjectResource($component);! $services = $component->getServicesDefinition();! foreach ($services->exports as $definition) {! $this->addDefinition($container, $definition, $component);! }! foreach ($services->definitions as $definition) {! $this->addDefinition($container, $definition, $component);! }! }!! $def = new DI\Definition($definition->class, $args);! $def->setPublic($definition->public);!! $container->setDefinition($component->getName() . '.' . $definition->name, $def);! }!}!
The Component Dependency Injector responsibility is to inject the definitions in a given container
![Page 77: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/77.jpg)
use Symfony\Component\HttpKernel\Kernel;!use Symfony\Component\Config\Loader\LoaderInterface;!!class AppKernel extends Kernel!{! public function registerBundles()! {! $bundles = array(! new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),! new Symfony\Bundle\MonologBundle\MonologBundle(),!! new SP\Core\Bundle\GameFrameworkBundle\GameFrameworkBundle([! new \SP\Core\Game\Framework\FrameworkComponent(),! new \SP\Core\Game\Framework\PaymentComponent(),! new \SP\Core\Game\Framework\AnalyticsComponent(),! ]),! );! }!}!
The AppKernel
These are the framework agnostic components that provide infrastructure and logic for the application
![Page 78: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/78.jpg)
namespace SP\Core\Bundle\GameFrameworkBundle;!!use Symfony\Component\DependencyInjection\Compiler\PassConfig;!use Symfony\Component\HttpKernel\Bundle\Bundle;!use Symfony\Component\DependencyInjection\ContainerBuilder;!!use SP\Core\Bridge\Symfony\DependencyInjection\ComponentDependencyInjector;!!class GameFrameworkBundle extends Bundle!{! private $components;!! function __construct(array $components = array())! {! $this->components = $components;! }!! public function build(ContainerBuilder $container)! {! $injector = new ComponentDependencyInjector($this->components);! $injector->registerComponentsDependencies($container);!! $container->addCompilerPass($injector, PassConfig::TYPE_BEFORE_REMOVING);! }!}!
The (in)Famous Framework Bundle
The components we are “installing” in this application
The “magic” is here !!!
![Page 79: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/79.jpg)
[SP\Core\Bridge\Symfony\DependencyInjection\Exception\ServiceNotFoundException] Component "core.game.framework" requires the service "core.game.framework.lock_system" as an implementation of "SP\Core\Component\Lock\LockSystem" but the service is not defined.
[SP\Core\Bridge\Symfony\DependencyInjection\Exception\ServiceNotFoundException] Component "core.game.payment" requires the service “core.game.payment.product_repository” as an implementation of "ProductRepository" but the service is not defined.
$ php app/console
The application complains about missing dependencies required by installed components
We are planning to add suggested implementations for this requirement
![Page 80: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/80.jpg)
services:!!! core.game.framework.lock_system:!! ! public: false!! ! class: SP\Core\Component\Lock\RedisLockSystem!! ! arguments:! redis: @sp.core.redis.connector.lock! timeout: 10! expiration: 10!
config.yml
services:!!! core.game.framework.lock_system:!! ! public: false!! ! class: SP\Core\Component\Lock\MemoryLockSystem!! ! arguments:! timeout: 10! expiration: 10!
config_test.yml
The application config Concrete implementation in the application (by default)
Semantic configuration?
Concrete implementation in the application (test mode)
![Page 81: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/81.jpg)
4- USING A CUSTOM INJECTOR
• Complete control of component dependencies
• Very limited in features (only constructor injections allowed)
• Framework agnostic
• Allows you to defer critical and volatile decisions
Pros Cons
• Very limited in features (only constructor injections allowed)
!
!
![Page 82: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/82.jpg)
CONCLUSIONS
• You have the tools, use the best tool that solve your problems
• Don’t be afraid of going to the limit with the framework you use
• The framework is just an implementation detail
![Page 83: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/83.jpg)
SOME REFERENCES
http://www.objectmentor.com/resources/articles/dip.pdf !http://www.martinfowler.com/articles/injection.html !http://martinfowler.com/articles/dipInTheWild.html !http://desymfony.com/ponencia/2013/inyeccion-dependencias-aplicaciones-php !http://fabien.potencier.org/article/11/what-is-dependency-injection !http://fabien.potencier.org/article/12/do-you-need-a-dependency-injection-container !http://fabien.potencier.org/article/13/introduction-to-the-symfony-service-container !https://www.youtube.com/watch?v=IKD2-MAkXyQ !http://richardmiller.co.uk/2014/03/12/avoiding-setter-injection/ !http://richardmiller.co.uk/2014/03/28/symfony2-configuring-different-services-for-different-environments/ !http://www.infoq.com/news/2013/07/architecture_intent_frameworks !http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html !
![Page 84: DIC To The Limit – deSymfonyDay, Barcelona 2014](https://reader034.vdocuments.net/reader034/viewer/2022051207/544337ceb1af9f410a8b48e2/html5/thumbnails/84.jpg)
THANK YOUFor your attention
@ronnylt