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. ABOUT ME ➤ Alexandre Balmes ➤ Independant Software Consultant ➤

    Co-fondateur de Vanoix ➤ J’aime le PHP et il me le rends bien ➤ Twitter : @pockystar
  2. <?php
 
 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;
 }
 }

  3. <?php
 
 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;
 }
 }
 <?php
 
 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
  4. 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
  5. “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
  6. OBJECTIFS ➤ Comprendre le métier du client ➤ Modéliser le

    métier ➤ Retranscrire le vocabulaire
  7. 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é”
  8. 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
  9. <?php
 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 ?
  10. 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
  11. 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
  12. 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)
  13. 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 ➤ …
  14. “ Moi quand je veux écrire une page, je met

    d’abord son titre et sa description puis je remplis son contenu. - Jane Doe
  15. “ 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
  16. 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
  17. 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
  18. 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)
  19. 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
  20. 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)
  21. “ Pour lire une page il faut qu’elle soit publiée

    - Jane Doe Action Propriété Méthode
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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 }
  27. 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)
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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