Applying Domain Driven Design when building Drupal modules with Symfony components

Applying Domain Driven Design when building Drupal modules with Symfony components

Talk delivered at DrupalCamp London 2015.

2bd48651cd01e0ca2e0a255a63da77aa?s=128

Marek Matulka

February 28, 2015
Tweet

Transcript

  1. 5.
  2. 6.
  3. 7.
  4. 9.
  5. 13.

    Requirement CMS - manage corporate website - manage other brands’

    websites - build seasonal microsites - integrate with Magento store
  6. 14.
  7. 17.

    Why Drupal? - open source CMS - easy to install

    and manage - most required functionality available out of the box
  8. 18.

    Why Drupal? - open source CMS - easy to install

    and manage - most required functionality available out of the box - easy to extend
  9. 20.
  10. 21.
  11. 26.
  12. 28.

    In order to create a good software, you have to

    know what the software is all about. – Eric Evans
  13. 35.

    Ubiquitous Language Ubiquitous Language Domain Expert Analyst Tester Developer Developer

    Application Code Acceptance Criteria Specs and documentation
  14. 36.
  15. 37.

    Example Feature: In order to see correct prices As a

    visitor I need to be able to locate the nearest depot Scenario: Given I am visiting a product page for the first time And I got prompted to locate my nearest depot When I enter my post code Then I should see my local depot information
  16. 38.

    Example Feature: In order to see correct prices As a

    visitor I need to be able to locate the nearest depot Scenario: Given I am visiting a product page for the first time And I got prompted to locate my nearest depot When I enter my post code Then I should see my local depot information
  17. 39.

    Domain namespace Acme\Depot; interface DepotLocator { /** * @param string

    $postcode * * @return Depot */ public function locateDepot($postcode); }
  18. 40.

    Implementation namespace Acme\MagentoAdapter; use Acme\Depot\DepotLocator; class MagentoDepotLocator implements DepotLocator {

    public function locateDepot($postcode) { return $this->entityMapper->mapDepot( $this->clientAdapter->findDepotByPostcode($postcode) );
  19. 42.

    Layered Architecture “High level modules should not depend on lower

    level implementation” – Robert C. Martin User Interface Application Domain Infrastructure
  20. 47.

    Clean Architecture Entities Use Cases Controllers Web Framework Drivers Interface

    Adapters Application Business Rules Enterprise Business Rules
  21. 55.

    Why clean architecture? - your code is clean - decoupled

    from framework - reusable - easy to test
  22. 56.

    Why clean architecture? - your code is clean - decoupled

    from framework - reusable - easy to test - easier to maintain
  23. 57.

    Why clean architecture? - your code is clean - decoupled

    from framework - re-usable - easy to test - easier to maintain - easier to change
  24. 58.
  25. 62.

    When to use DDD? - complex domain - access to

    domain experts - iterative, long term process
  26. 63.

    When to use DDD? - complex domain - access to

    domain experts - iterative, long term process - OO design
  27. 67.

    When not to use DDD? - simple domain - data

    centric domains - prototyping
  28. 69.

    composer.json "require": { "php": ">=5.4.0", "symfony/console": "~2.5", "drush/drush": "7.*@dev", "symfony/finder":

    "~2.5", "symfony/dependency-injection": "~2.5", "symfony/config": "~2.5", "symfony/filesystem": "~2.6", "doctrine/dbal": "~2.5", "rhumsaa/uuid": "~2.8" },
  29. 70.

    AppKernel.php public function boot() { $this->container = new ContainerBuilder(); $this->container->registerExtension(new

    ApplicationExtension()); $loader = new YamlFileLoader($this->container, new FileLocator('app/config')); $loader->load('parameters.yml'); $loader = new XmlFileLoader($this->container, new FileLocator('/Resources/config')); $loader->load('services.xml'); $this->container->compile(); } public function getContainer() { return $this->container; }
  30. 72.

    database.php $databases = [ 'default' => [ 'default' => [

    'database' => $conf['di_container']->getParameter('database.name'), 'username' => $conf['di_container']->getParameter('database.username'), 'password' => $conf['di_container']->getParameter('database.password'), 'host' => $conf['di_container']->getParameter('database.host'), 'port' => $conf['di_container']->getParameter('database.port'), 'driver' => 'mysql', 'prefix' => '', ], ], ];
  31. 73.

    depot_locator.module function get_depot_json($postcode) { global $conf; $depotEntity = $conf['di_container']->get('acme.depot_locator') ->locateDepot($postcode);

    $depot = [ 'code' => $depotEntity->getCode(), 'name' => $depotEntity->getName(), 'postcode' => $depotEntity->getPostcode(), ]; return ['depot' => $depot]; }