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

Applying Domain Driven Design when building Dru...

Marek Matulka
February 28, 2015

Applying Domain Driven Design when building Drupal modules with Symfony components

Talk delivered at DrupalCamp London 2015.

Marek Matulka

February 28, 2015
Tweet

More Decks by Marek Matulka

Other Decks in Programming

Transcript

  1. Requirement CMS - manage corporate website - manage other brands’

    websites - build seasonal microsites - integrate with Magento store
  2. Why Drupal? - open source CMS - easy to install

    and manage - most required functionality available out of the box
  3. Why Drupal? - open source CMS - easy to install

    and manage - most required functionality available out of the box - easy to extend
  4. In order to create a good software, you have to

    know what the software is all about. – Eric Evans
  5. Ubiquitous Language Ubiquitous Language Domain Expert Analyst Tester Developer Developer

    Application Code Acceptance Criteria Specs and documentation
  6. 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
  7. 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
  8. Domain namespace Acme\Depot; interface DepotLocator { /** * @param string

    $postcode * * @return Depot */ public function locateDepot($postcode); }
  9. Implementation namespace Acme\MagentoAdapter; use Acme\Depot\DepotLocator; class MagentoDepotLocator implements DepotLocator {

    public function locateDepot($postcode) { return $this->entityMapper->mapDepot( $this->clientAdapter->findDepotByPostcode($postcode) );
  10. Layered Architecture “High level modules should not depend on lower

    level implementation” – Robert C. Martin User Interface Application Domain Infrastructure
  11. Clean Architecture Entities Use Cases Controllers Web Framework Drivers Interface

    Adapters Application Business Rules Enterprise Business Rules
  12. Why clean architecture? - your code is clean - decoupled

    from framework - reusable - easy to test
  13. Why clean architecture? - your code is clean - decoupled

    from framework - reusable - easy to test - easier to maintain
  14. Why clean architecture? - your code is clean - decoupled

    from framework - re-usable - easy to test - easier to maintain - easier to change
  15. When to use DDD? - complex domain - access to

    domain experts - iterative, long term process
  16. When to use DDD? - complex domain - access to

    domain experts - iterative, long term process - OO design
  17. When not to use DDD? - simple domain - data

    centric domains - prototyping
  18. 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" },
  19. 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; }
  20. 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' => '', ], ], ];
  21. 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]; }