Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Separation of concerns - DPC 12

Joshua Thijssen
June 08, 2012
440

Separation of concerns - DPC 12

Joshua Thijssen

June 08, 2012
Tweet

Transcript

  1. Separation of Concerns

    View Slide

  2. Separation of concerns
    About Joshua
     Joshua Thijssen, NoxLogic / Techademy
     Freelance Consultant, Developer, Trainer
     Development in PHP, Python, Perl, C, Java
    [email protected]
     @jaytaph

    View Slide

  3. Separation of concerns
    About Stephan
     Stephan Hochdörfer, bitExpert AG
     Department Manager Research Labs
     enjoying PHP since 1999
    [email protected]
     @shochdoerfer

    View Slide

  4. Separation of concerns
    It is what I sometimes have called the
    separation of concerns, which [...] is
    yet the only available technique for
    effective ordering of one's thoughts.
    Edsger W. Dijkstra, "On the role of scientific thought" (1974)

    View Slide

  5. Separation of concerns
    SoC […] is the process of breaking a
    computer program into distinct
    features that overlap in functionality as
    little as possible.
    http://en.wikipedia.org/wiki/Separation_of_concerns

    View Slide

  6. Separation of concerns
    It`s all about focusing!

    View Slide

  7. Separation of concerns
    One step a time. Focus on the details!

    View Slide

  8. Separation of concerns
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
    $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }
    $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident");
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }
    foreach($items as $item) {
    echo $item['message'];
    }

    View Slide

  9. Separation of concerns
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
    $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }
    $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident");
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }
    foreach($items as $item) {
    echo $item['message'];
    }}
    Controller part!

    View Slide

  10. Separation of concerns
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
    $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }
    $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident");
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }
    foreach($items as $item) {
    echo $item['message'];
    }
    View part!

    View Slide

  11. Separation of concerns
    Very unstable. Not safe for changes....

    View Slide

  12. Separation of concerns
    „Make everything as simple as possible...“

    View Slide

  13. Separation of concerns
    Establish boundaries

    View Slide

  14. Separation of concerns
    What are the boundaries?
    Presentation Layer

    View Slide

  15. Separation of concerns
    What are the boundaries?
    Presentation Layer
    Business Layer

    View Slide

  16. Separation of concerns
    What are the boundaries?
    Presentation Layer
    Business Layer
    Resource Access Layer

    View Slide

  17. Isolate functionality, break it apart!
    Separation of concerns

    View Slide

  18. Separation of concerns
    namespace Acme\DemoBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    class DemoController extends Controller {
    /**
    * @Route("/", name="_demo")
    * @Template()
    */
    public function indexAction() {
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access_to...");
    $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }

    View Slide

  19. Separation of concerns
    $feed = file_get_contents("http://search.twitter.com/search.json?...");
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }
    return array('items' => $items);
    }
    }

    View Slide

  20. Focus on one responsibility a time!
    Separation of concerns

    View Slide

  21. Separation of concerns
    namespace Acme\DemoBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    class DemoController extends Controller {
    /**
    * @Route("/", name="_demo")
    * @Template()
    */
    public function indexAction() {
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access_to...");
    $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }
    Facebook connector

    View Slide

  22. Separation of concerns
    $feed = file_get_contents("http://search.twitter.com/search.json?..." );
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }
    return array('items' => $items);
    }
    }
    Twitter connector

    View Slide

  23. Interfaces as a contract
    Separation of concerns

    View Slide

  24. Separation of concerns
    interface ConnectorInterface {
    /**
    * Will return an array of the latest posts.
    * return array
    */
    public function getLatestPosts() {
    }
    }

    View Slide

  25. Separation of concerns
    class FacebookConnector implements ConnectorInterface {
    protected $handle;
    public function __construct($handle) {
    $this->handle = $handle;
    }
    public function getLatestPosts() {
    $items = array();
    $token = file_get_contents("https://graph.facebook.com/oauth/access...");
    $feed = file_get_contents("https://graph.facebook.com/.
    $this->handle."/feed?".$token);
    $feed = json_decode($feed, true);
    foreach($feed['data'] as $post) {
    if('123456789012' === $post['from']['id']) {
    $items[] = array(
    'date' => strtotime($post['created_time']),
    'message' => $post['message']);
    }
    }
    return $items;
    }
    }

    View Slide

  26. Separation of concerns
    class FacebookConnector implements ConnectorInterface {
    protected $handle;
    public function __construct($handle) {
    $this->handle = $handle;
    }
    public function getLatestPosts() {
    $token = $this->getAccessToken();
    return $this->readPosts($token);
    }
    protected function getAccessToken() {
    return file_get_contents("https://graph.facebook.com/oauth/access_?...");
    }
    protected function readPosts($token) {
    // read the post, filter all relevant and return them...
    }
    }

    View Slide

  27. Separation of concerns
    class FacebookConnectorV2 extends FacebookConnector {
    protected function getAccessToken() {
    return md5($this->handle);
    }
    }
    Easy to extend, will not influence
    the behaviour of other methods!

    View Slide

  28. Separation of concerns
    namespace Acme\DemoBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    class DemoController extends Controller {
    /**
    * @Route("/", name="_demo")
    * @Template()
    */
    public function indexAction() {
    $items = array();
    $fb = $this->get('FbConnector');
    $items += $fb->getLatestPosts();
    $feed = file_get_contents("http://search.twitter.com/search.json?...");
    $feed = json_decode($feed, true);
    foreach($feed['results'] as $tweet) {
    $items[] = array(
    'date' => strtotime($tweet['created_at']),
    'message' => $tweet['text']);
    }

    View Slide

  29. Separation of concerns
    namespace Acme\DemoBundle\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    class DemoController extends Controller {
    /**
    * @Route("/", name="_demo")
    * @Template()
    */
    public function indexAction() {
    $items = array();
    $fb = $this->get('FbConnector');
    $items += $fb->getLatestPosts();
    $twitter = $this->get('TwitterConnector');
    $items += $twitter->getLatestPosts();
    return array('items' => $items);
    }
    }

    View Slide

  30. Will improve the testability!
    Separation of concerns

    View Slide

  31. Will reduce tight coupling!
    Separation of concerns

    View Slide

  32. Will increase component reuse!
    Separation of concerns

    View Slide

  33. The value of separation
    Why should I do it?
    Separation of concerns

    View Slide

  34. The value of separation
    1. Getting rid of code duplication
    Separation of concerns

    View Slide

  35. The value of separation
    2. Application becomes more stable
    and easier to understand
    Separation of concerns

    View Slide

  36. The value of separation
    3. Single responsibility offers clear
    extension points
    Separation of concerns

    View Slide

  37. The value of separation
    4. Increases the reusability of
    components
    Separation of concerns

    View Slide

  38. Separation of concerns
    How to separate the concerns?

    View Slide

  39. Separation of concerns
    How to separate? Horizontal Separation
    Presentation Layer
    Business Layer
    Resource Access Layer

    View Slide

  40. Separation of concerns
    How to separate? Horizontal Separation
    ./symfony
    |-app
    |---cache
    |---config
    |---Resources
    |-src
    |---Acme
    |-----DemoBundle
    |-------Controller
    |-------Resources
    |---------config
    |---------public
    |-----------css
    |-----------images
    |---------views
    |-----------Demo
    |-----------Welcome
    |-------Tests
    |---------Controller
    |-web

    View Slide

  41. Separation of concerns
    How to separate? Service Separation
    Service Interface Layer
    Business Layer
    Resource Access Layer

    View Slide

  42. Separation of concerns
    How to separate? Service Separation
    Frontend / UI Webservers
    REST API
    Datastore
    Solr Search Service

    View Slide

  43. Separation of concerns
    How to separate? Vertical Separation
    Module A Module B Module C

    View Slide

  44. Separation of concerns
    How to separate? Vertical Separation
    ./symfony
    |-app
    |---cache
    |---config
    |---Resources
    |-src
    |---Acme
    |-----AdminBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-----ProductsBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-----CustomersBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-web

    View Slide

  45. Separation of concerns
    How to separate? Vertical Separation
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer

    View Slide

  46. Separation of concerns
    How to separate? Vertical Separation
    ./symfony
    |-app
    |---cache
    |---config
    |---Resources
    |-src
    |---Acme
    |-----AdminBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-----ProductsBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-----CustomersBundle
    |-------Controller
    |-------Resources
    |-------Tests
    |-web

    View Slide

  47. Cross-cutting concerns?
    How to deal with security or logging
    aspects?
    Separation of concerns

    View Slide

  48. Separation of concerns
    How to separate? Aspect Separation
    Presentation Layer
    Business Layer
    Resource Access Layer
    Aspects

    View Slide

  49. Separation of concerns
    How to separate? Aspect Separation
    namespace Acme\Products\Aspects;
    /**
    * @FLOW3\Aspect
    */
    class LoggingAspect {
    /**
    * @FLOW3\Inject
    * @var \Acme\Logger\LoggerInterface
    */
    protected $logger;
    /**
    * @param \TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint
    * @FLOW3\Before("method(Acme\Products\Model\Product->delete())")
    */
    public function log(\TYPO3\FLOW3\AOP\JoinPointInterface $jp) {
    $product = $jp->getMethodArgument('product');
    $this->logger->info('Removing ' .
    $product->getName());
    }
    }

    View Slide

  50. Separation of concerns
    Dependency Direction

    View Slide

  51. Separation of concerns
    Dependency Direction
    "High-level modules should not
    depend on low-level modules.
    Both should depend on
    abstractions."
    Robert C. Martin

    View Slide

  52. Separation of concerns
    Inverting Concerns
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer

    View Slide

  53. Separation of concerns
    Inverting Concerns
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer
    Presentation
    Layer
    Business
    Layer
    Resource
    Access Layer
    UI
    Component

    View Slide

  54. Separation of concerns
    Inverting Concerns
    The goal of Dependency Injection
    is to separate the concerns of
    obtaining the dependencies from
    the core concerns of a component.

    View Slide

  55. Separation of concerns
    What are the benefits? Let`s recap....

    View Slide

  56. Separation of concerns
    What are the benefits?
    1. Facilitate reusability

    View Slide

  57. Separation of concerns
    What are the benefits?
    2. Ensure the maintainability

    View Slide

  58. Separation of concerns
    What are the benefits?
    3. Increasing the code quality

    View Slide

  59. Separation of concerns
    What are the benefits?
    4. Enables everyone to understand
    the application

    View Slide

  60. Separation of concerns
    What are the benefits?
    5. Allows developers to work
    on components in isolation

    View Slide

  61. Thank you!

    View Slide

  62. http://joind.in/6244

    View Slide