Decoupling from the framework

Decoupling from the framework

A framework, by definition, provides a basic set of tools for application development we can use to avoid writing repeatable code. It often encourages to take shortcuts to enable rapid development. In theory, we only need to implement the part which is specific to our domain. In practice, we often end up with highly coupled code, mixed layers and a dependency graph deceptively close to spaghetti.
Jakub will take Symfony as an example to present techniques of decoupling from a framework and keeping it on a distance.

1a4e1f98f3aeef310273366c8c785207?s=128

Jakub Zalas

April 07, 2014
Tweet

Transcript

  1. 4.

    "MODULES ARE COUPLED IF CHANGING ONE OF THEM REQUIRES CHANGING

    ANOTHER ONE." http://martinfowler.com/ieeeSoftware/coupling.pdf Martin Fowler
  2. 14.
  3. 17.
  4. 18.

    use Buzz\Browser; class PackageCrawler { public function crawl($resource) { $browser

    = new Browser(); $response = $browser->get($resource); // @todo parse } }
  5. 20.

    use Buzz\Browser; class PackageCrawler { private $browser; public function __construct(Browser

    $browser) { $this->browser = $browser; } public function crawl($resource) { $response = $this->browser->get($resource); // @todo parse } }
  6. 22.

    use Buzz\Browser; class PackageCrawler { private $browser; public function __construct(Browser

    $browser) { $this->browser = $browser; } public function crawl($resource, Container $container) { $user = $container->get('security.context') ->getToken()->getUser(); $response = $this->browser->get( $resource, array('X-User' => $user->getId()) ); // @todo parse } }
  7. 24.

    use Buzz\Browser; class PackageCrawler { private $browser; public function __construct(Browser

    $browser) { $this->browser = $browser; } public function crawl($resource, $userId) { $response = $this->browser->get( $resource, array('X-User' => $userId) ); // @todo parse } }
  8. 26.

    use Buzz\Browser; use Symfony\Component\Console\Input\InputInterface; class PackageCrawler { private $browser; public

    function __construct(Browser $browser) { $this->browser = $browser; } public function crawl(InputInterface $input) { $response = $this->browser->get( $input->getOption('resource') ); // @todo parse } }
  9. 27.

    use Buzz\Browser; class PackageCrawler { private $browser; public function __construct(Browser

    $browser) { $this->browser = $browser; } public function crawl($resource) { $response = $this->browser->get( $resource ); // @todo parse } }
  10. 30.

    use Buzz\Browser; class PackageCrawler { private $browser; public function __construct(Browser

    $browser) { $this->browser = $browser; } public function crawl($resource) { $response = $this->browser->get($resource); // @todo parse } }
  11. 31.
  12. 32.
  13. 34.

    class PackageCrawler { private $httpClient; public function __construct(HttpClient $httpClient) {

    $this->httpClient = $httpClient; } public function crawl($resource) { $response = $this->httpClient->get($resource); // @todo parse } }
  14. 35.
  15. 36.
  16. 37.

    use Buzz\Browser; class BuzzClient implements HttpClient { private $browser; public

    function __construct(Browser $browser) { $this->browser = $browser; } public function get($resource, array $headers) { return (string) $this->browser->get( $resource, $headers ); } }
  17. 38.

    use Guzzle\Client; class GuzzleClient implements HttpClient { private $guzzle; public

    function __construct(Client $guzzle) { $this->guzzle = $guzzle; } public function get($resource, array $headers) { $request = $this->guzzle->createRequest( 'GET', $resource ); $response = $request->send(); return $response->getBody(); } }
  18. 42.

    use Symfony\Bundle\FrameworkBundle\Controller\Controller; class SearchController extends Controller { public function searchAction(Request

    $request) { $keywords = $request->query->get('keywords'); $products = $this->getDoctrine() ->getRepository('Acme:Product') ->search($keywords); return $this->render( 'template.html.twig', array('products' => $products) ); } }
  19. 50.

    "Create your application to work without either a UI or

    a database […]" http://alistair.cockburn.us/Hexagonal+architecture
  20. 54.
  21. 56.

    use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; class SearchController { private $templating; private

    $doctrine; public function __construct( EngineInterface $templating, ManagerRegistry $doctrine ) { $this->templating = $templating; $this->doctrine = $doctrine; } }
  22. 57.

    // .. class SearchController { // ... public function searchAction(Request

    $request) { $keywords = $request->query->get('keywords'); $repository = $this->doctrine ->getRepository('Acme:Product') ->search($keywords); return $this->templating->renderResponse( 'template.html.twig', array('products' => $products) ); } }
  23. 59.

    use Doctrine\Common\Persistence\ManagerRegistry; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; class SearchController { private $templating; private

    $doctrine; public function __construct( EngineInterface $templating, ManagerRegistry $doctrine ) { $this->templating = $templating; $this->doctrine = $doctrine; } }
  24. 60.

    use Acme\ProductBundle\Entity\ProductRepository; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; class SearchController { private $templating; private

    $repository; public function __construct( EngineInterface $templating, ProductRepository $repository ) { $this->templating = $templating; $this->repository = $repository; } }
  25. 61.

    // .. class SearchController { // ... public function searchAction(Request

    $request) { $keywords = $request->query->get('keywords'); $repository = $this->repository->search($keywords); return $this->templating->renderResponse( 'template.html.twig', array('products' => $products) ); } }
  26. 63.

    namespace Acme\ProductCatalog; interface ProductRepository { /** * @param string $keywords

    * * @return Product[] */ public function search($keywords); }
  27. 64.

    use Acme\ProductCatalog\ProductRepository; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; class SearchController { private $templating; private

    $repository; public function __construct( EngineInterface $templating, ProductRepository $repository ) { $this->templating = $templating; $this->repository = $repository; } }
  28. 65.
  29. 74.
  30. 77.