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

Pourquoi le DDD ne devrait rien changer à votre vie

Pourquoi le DDD ne devrait rien changer à votre vie

Alexandre Balmes

January 30, 2016
Tweet

More Decks by Alexandre Balmes

Other Decks in Programming

Transcript

  1. POURQUOI LE DDD

    NE DEVRAIT RIEN CHANGER
    A VOTRE VIE

    View full-size slide

  2. ABOUT ME
    ➤ Alexandre Balmes
    ➤ Independant Software Consultant
    ➤ Co-fondateur de Vanoix
    ➤ J’aime le PHP et il me le rends bien
    ➤ Twitter : @pockystar

    View full-size slide

  3. AU COMMENCEMENT

    View full-size slide


  4. C’est pas grave, je ferais l’introduction
    -Alexandre Balmes

    View full-size slide

  5. J’AI UN PROBLEME

    View full-size slide

  6. J’AI UN PROBLEME

    JE DOIS VOUS PARLER DE DDD

    View full-size slide

  7. J’AI UN PROBLEME
    JE DOIS VOUS PARLER DE DDD
    EN PHP

    View full-size slide


  8. namespace AppBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;

    /**

    * Class Stuff

    *

    * @ORM\Entity()

    */

    class Stuff

    {

    /**

    * @ORM\Column(type="integer")

    * @ORM\Id

    * @ORM\GeneratedValue(strategy="AUTO")

    */

    protected $id;


    /**

    * @ORM\Column(type="string", length=255)

    */

    protected $title;


    public function getId()

    {

    return $this->id;

    }


    public function setTitle($title)

    {

    $this->title = $title;

    }


    public function getTitle()

    {

    return $this->title;

    }

    }


    View full-size slide


  9. namespace AppBundle\Entity;


    use App\Domain\Entity\Stuff as BaseStuff;


    /**

    * Class Stuff

    */

    class Stuff extends BaseStuff

    {

    protected $id;


    public function getId() : int

    {

    return $this->id;

    }

    }


    namespace App\Domain\Entity;

    use App\Domain\ValueObject\StuffId;

    /**

    * Class Stuff
    */

    class Stuff

    {

    protected $stuffId;


    protected $title;


    private function __construct();


    public static function buildStuffFromFoobar(StuffId $id, string
    $title)

    {

    $this->stuffId = $id;

    $this->title = $title;

    }
    public function getStuff() : array

    {

    return [

    "id" => $this->stuffId,

    "title" => $this->title,

    ];

    }
    }


    AppBundle\Entity:
    type: entity
    id:

    id:

    type: integer

    generator: { strategy: AUTO }
    App\Domain\Entity\Stuff:

    type: mappedSuperclass

    fields:

    title: { type: string }

    embedded:

    stuffId:

    class: App\Domain\ValueObject\StuffId

    View full-size slide

  10. DOMAIN-DRIVEN DESIGN

    View full-size slide

  11. HISTORIQUE
    ➤ Un homme : Eric Evans
    ➤ Un livre : Domain-Driven Design: Tackling Complexity in the Heart of Software
    (2003 - Addison Wesley)
    ➤ Une idéologie : DDD => Software Craftsmanship => XP => TDD

    View full-size slide

  12. “Leading software designers have recognized domain
    modeling and design as critical topics for at least 20
    years, yet surprisingly little has been written about what
    needs to be done or how to do it. Although it has never
    been formulated clearly, a philosophy has emerged as an
    undercurrent in the object community, a philosophy I
    call domain-driven design.
    -Eric Evans

    View full-size slide

  13. OBJECTIFS
    ➤ Comprendre le métier du client
    ➤ Modéliser le métier
    ➤ Retranscrire le vocabulaire

    View full-size slide

  14. POURQUOI ?
    ➤ Le coeur d’une application c’est son métier (= son Domain)
    ➤ L’interface graphique, les services tiers peuvent régulièrement changer mais le
    métier beaucoup moins
    ➤ Le métier peut évoluer, stagner, disparaitre mais il ne devrait pas être “réinitialisé”

    View full-size slide

  15. CONSEQUENCES
    ➤ Le DDD est une boite à outil :
    ➤ SOLID
    ➤ regroupant des bonnes pratiques de conception
    ➤ permettant la communication entre porteur de projet et développeurs à travers un
    language commun

    View full-size slide

  16. PLUTÔT NORMAL NON ?

    View full-size slide

  17. namespace AppBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;
    /**

    * Class Stuff

    *

    * @ORM\Entity()

    */

    class Stuff

    {

    /**

    * @ORM\Column(type="integer")

    * @ORM\Id

    * @ORM\GeneratedValue(strategy="AUTO")

    */

    protected $id;

    /**

    * @ORM\Column(type="string", length=255)

    */

    protected $title;

    public function getId()

    {

    return $this->id;

    }

    public function setTitle($title)

    {

    $this->title = $title;

    }

    public function getTitle()

    {

    return $this->title;

    }

    }

    ALORS POURQUOI ?

    View full-size slide

  18. EST-IL ACCEPTABLE 

    POUR VOUS DE :

    View full-size slide

  19. PASSER DES HEURES A
    ESSAYER DE DECRYPTER DU
    CODE ?

    View full-size slide

  20. DE NE PAS POUVOIR DEVELOPER
    CERTAINES FONCTIONNALITÉS
    SOUS PRETEXTE DE ?

    View full-size slide

  21. D’ETRE BLOQUÉ PAR LA
    TECHNO ?

    View full-size slide

  22. DES IDÉES SIMPLES
    Qui changent un peu la vie

    View full-size slide

  23. UBIQUITOUS LANGUAGE
    ➤ “N’importe quel utilisateur” doit pouvoir comprendre le code
    ➤ Le code doit refléter le language du métier, son vocabulaire, ses termes…
    ➤ Il faut chercher les experts métiers et les confronter au code
    ➤ Le business comprend le dévelopeur, le dévelopeur comprends le business

    View full-size slide

  24. LAYERED ARCHITECTURE
    User Interface
    Application
    Domain
    Infrastructure
    Form
    Specification
    Service
    Entity

    ValueObject
    Enum

    DomainEvents
    Bridge

    CQRS

    Listener

    Persistence
    DTO

    Serializer

    Service

    Repository

    Services

    Mailer


    Controller
    Action
    Responder

    View full-size slide

  25. BOUNDED CONTEXT
    ➤ Une information n’a pas de sens sans contexte,
    ➤ Il est important de décomplexifier une application en la segmentant suivant ses
    contextes
    ➤ Une “Context map” permet de savoir qui travaille avec qui et de quelle manière (les
    collaborateurs)
    ➤ ProTip : Un channel monolog par contexte (plus d’info : http://www.slideshare.net/
    javier.eguiluz/new-symfony-tips-tricks-symfonycon-paris-2015)

    View full-size slide

  26. ITERATIVE DEVELOPMENT
    ➤ De l’agile tout simplement
    ➤ Chaque modification du language peut provoquer :
    ➤ Une modification du Domain
    ➤ L’ajout d’un contexte
    ➤ Du code d’Infrastructure ou d’Application en plus ou en moins
    ➤ …

    View full-size slide

  27. C’EST CA LE DOMAIN-DRIVEN
    DESIGN

    View full-size slide

  28. MISE EN PRATIQUE
    Des patterns que l’on devrait tous utiliser

    View full-size slide


  29. Moi quand je veux écrire une page, je met
    d’abord son titre et sa description puis je remplis
    son contenu.
    - Jane Doe

    View full-size slide


  30. Moi quand je veux écrire une page, je met
    d’abord son titre et sa description puis je remplis
    son contenu.
    - Jane Doe
    Auteur Action
    Constructeur
    Propriétés

    View full-size slide

  31. Page
    __construct(Author $author, string $title, string $description)
    writeContent(string $content)
    Author
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    CreatePageAction::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain

    View full-size slide

  32. Page
    __construct(Author $author, string $title, string $description)
    writeContent(string $content)
    Author
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    CreatePageAction::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain
    PageService::create(string $author, string, $title, $string description)
    Infrastructure

    View full-size slide

  33. Page
    __construct(AuthorId $uid, Author $author,
    string $title, string $description)
    writeContent(string $content)
    CreatePageAction::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain
    PageService::create(string $author, string, $title, $string description)
    Infrastructure
    PageRepository::__construct(Page::class)
    getClassName() : string
    save()
    Author + AuthorId
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    NewPageHasBeenCreatedEv
    ent::__construct(Page $page)

    View full-size slide

  34. CreatePageAction::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain
    PageService::create(CreatePageDTO $dto)
    Infrastructure
    CreatePageDTO::__construct(string $author, string $title, string $description)
    Page
    __construct(AuthorId $uid, Author $author,
    string $title, string $description)
    writeContent(string $content)
    PageRepository::__construct(Page::class)
    getClassName() : string
    save()
    Author + AuthorId
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    NewPageHasBeenCreatedEv
    ent::__construct(Page $page)
    Application

    View full-size slide

  35. Application + UI
    Domain
    Infrastructure
    CreatePageCommand::__construct(Author $author, string $title, string $description)
    CreatePageHandler::handle(CreatePageCommand $command)
    PageService::create(CreatePageDTO $dto)
    PageWriteRepository::__construct(Page::class)
    getClassName() : string
    Page
    __construct(AuthorId $uid, Author $author,
    string $title, string $description)
    writeContent(string $content)
    Author + AuthorId
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    NewPageHasBeenCreatedEv
    ent::__construct(Page $page)

    View full-size slide


  36. Pour lire une page, il faut qu’elle soit publiée
    - Jane Doe

    View full-size slide


  37. Pour lire une page il faut qu’elle soit publiée
    - Jane Doe
    Action Propriété
    Méthode

    View full-size slide

  38. Page
    __construct(Author $author, string $title, string $description)
    writeContent(string $content)
    publish()
    read() : array
    Author
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    ReadPage::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain

    View full-size slide

  39. Page
    __construct(Author $author, string $title, string $description)
    writeContent(string $content)
    publish()
    read() : array
    Author
    __construct(string $author)
    getValue() : string
    isSatisfiedBy() : boolean
    ReadPage::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain
    PageService::read(PageId $id) : Page
    Infrastructure
    PageRepository::__construct(Page::class)
    getClassName() : string
    findById(PageId $id) : Page

    View full-size slide

  40. ReadPage::__invoke(ServerRequestInterface $request)
    UserInterface
    Domain
    PageService::read(PageId $id) : Page
    Infrastructure
    PageORMRepository::__construct(ObjectManager $manager, Page::class)
    getClassName() : string
    findById(PageId $id) : Page
    PageIsReadableSpecification::isSatisfiedBy(Page $page) :
    boolean
    Application

    View full-size slide

  41. ET MON BUNDLE/PLUGIN/MODULE ?

    View full-size slide

  42. HEXAGONAL
    Infrastructure
    Application
    DOMAIN
    Adapter

    /Bridge
    Adapter

    /Bridge
    Adapter

    /Bridge
    Adapter

    /Bridge
    Adapter

    /Bridge
    Adapter

    /Bridge
    Persistence
    Mailer
    Listener
    CQRS
    API Client
    Bundle

    Plugin

    Module

    View full-size slide

  43. ET MON SYMFONY ?
    ➤ Adoptez PSR-4 !
    {

    "name": "black-project/black-standard-edition",

    "license": "MIT",

    "type": "project",

    "description": "The \"Black Edition\" distribution build on top of \"Symfony Standard
    Edition\"",

    "autoload": {

    "psr-4": {

    "Black\\Website\\": "src/Website/",

    "Black\\Bundle\\WebsiteBundle\\": "src/Website/Infrastructure/Bridge/Symfony/
    WebsiteBundle/"

    },

    "classmap": [ "app/AppKernel.php", "app/AppCache.php" ]

    },
    // Stuff
    }

    View full-size slide

  44. MON DDD
    Le mien à moi que j’aime

    View full-size slide

  45. SYMFONY-LESS

    View full-size slide

  46. SYMFONY-LESS
    ➤ Mais utilisant des composants Symfony
    ➤ Mais pouvant être facilement encapsulé dans Symfony ou autre chose (via les
    Bridge)
    ➤ Mais surtout pouvant être utilisé dans un Middleware
    ➤ Puli : Universal Packages for PHP (Resource access et discovery)

    View full-size slide

  47. SYMFONY-LESS
    ➤ Directory structure :
    ➤ src/Application//[…]
    ➤ src/Domain//[…]
    ➤ src/Infrastructure//[…]
    ➤ res/config
    ➤ res/views

    View full-size slide

  48. PHP7 FOR THE WIN

    View full-size slide

  49. PHP7 FOR THE WIN
    ➤ PHP7 est version majeure
    ➤ Improved performance: PHP 7 is up to twice as fast as PHP 5.6
    ➤ Significantly reduced memory usage
    ➤ Abstract Syntax Tree
    ➤ Many fatal errors converted to Exceptions
    ➤ Secure random number generator
    ➤ The null coalescing operator (??)
    ➤ Return and Scalar Type Declarations
    ➤ Anonymous Classes

    View full-size slide

  50. PHP7 FOR THE WIN
    ➤ PHP7 est version majeure
    ➤ Improved performance: PHP 7 is up to twice as fast as PHP 5.6
    ➤ Significantly reduced memory usage
    ➤ Abstract Syntax Tree
    ➤ Many fatal errors converted to Exceptions
    ➤ Secure random number generator
    ➤ The null coalescing operator (??)
    ➤ Return and Scalar Type Declarations
    ➤ Anonymous Classes

    View full-size slide

  51. PSR-7 FOR THE WIN

    View full-size slide

  52. PSR-7 FOR THE WIN
    ➤ Standardisation des échanges :
    ➤ La requête : ServerRequestInterface
    ➤ La réponse : ResponseInterface
    ➤ Une URI : UriInterface
    ➤ Un fichier uploadé : UploadedInterface

    ➤ Utilisé dans notamment :
    ➤ Slim3
    ➤ Zend3 : Stratigility/Diactoros/Expressive
    ➤ Symfony via symfony/psr-http-message-bridge

    View full-size slide

  53. ACTION - DOMAIN - RESPONDER

    View full-size slide

  54. ACTION - DOMAIN - RESPONDER
    ➤ J’ai dit bye bye au MVC :
    ➤ Un contrôleur -> X actions avec toutes les dépendances = mauvaise idée
    ➤ Un contrôleur -> 1 action avec ses propres dépendances = meilleure idée

    View full-size slide

  55. ACTION - DOMAIN - RESPONDER
    ➤ J’ai dit bonjour à l’ADR :
    ➤ Déjà le naming est “parlant”
    ➤ Une Action de type Callable dont l’objectif est de traiter une requête et d’interagir
    avec le Domain
    ➤ Un Responder de type Callable dont l’objectif est de présenter les données
    provenants de l’Action
    ➤ C’est possible avec Symfony depuis la version 2.6

    View full-size slide

  56. BDD/SPECBDD
    DOMAIN MODELING

    View full-size slide

  57. CONCLUSION
    Si je devais résumer

    View full-size slide

  58. J’AIME LE DDD

    View full-size slide

  59. PARCE QU’IL DONNE DU SENS 

    A MON TRAVAIL
    SUR LE LONG TERME

    View full-size slide

  60. PARCE QU’IL CADRE

    MON CODE

    SUR LE LONG TERME

    View full-size slide

  61. PARCE QUE MES CLIENTS

    APPRECIENT DE PARTAGER

    View full-size slide

  62. DES MOMENTS JE NE 

    FAIS PAS DE DDD

    View full-size slide

  63. J’UTILISE LES BONNES IDEES
    DU DDD DANS UN CONTEXTE PRECIS

    View full-size slide

  64. J’UTILISE DES DESIGN PATTERNS
    MIS EN AVANT DANS LE DDD

    View full-size slide

  65. ET CA FONCTIONNE TRÈS BIEN

    View full-size slide

  66. J’AIME SYMFONY/SYMFONY

    View full-size slide

  67. C’EST GRACE A LUI (EUX) QUE
    JE SUIS DEVANT VOUS

    View full-size slide

  68. MAIS JE NE PEUX PAS

    MISER MA CARIERE SUR SYMFONY

    View full-size slide

  69. PARCE QUE LE METIER DETERMINE
    LES OUTILS PAS L’INVERSE

    View full-size slide