DDD: Retour d'expérience
-
Upload
gbprod -
Category
Technology
-
view
196 -
download
0
Transcript of DDD: Retour d'expérience
![Page 1: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/1.jpg)
DDD: Retour d’expérience@GillesRoustan
![Page 2: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/2.jpg)
Ma façon d’apprendre
![Page 3: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/3.jpg)
I can’t decide
![Page 4: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/4.jpg)
Pourquoi DDD ?
![Page 5: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/5.jpg)
/** * @ORM\Table(name="oc_advert") * @ORM\Entity(repositoryClass="OC\PlatformBundle\Repository\AdvertRepository") * @ORM\HasLifecycleCallbacks() */class Advert{ /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id;
/** * @ORM\Column(name="date", type="datetime") * @Assert\DateTime() */ private $date;
/** * @ORM\Column(name="author", type="string", length=255) * @Assert\Length(min=2) */ private $author;
/** * @ORM\Column(name="content", type="text") * @Assert\NotBlank() */ private $content;
/** * @ORM\OneToOne(targetEntity="OC\PlatformBundle\Entity\Image", cascade={"persist", "remove"}) * @Assert\Valid() */
Code first
![Page 6: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/6.jpg)
![Page 7: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/7.jpg)
DDD to the rescue
![Page 8: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/8.jpg)
4 couches
![Page 9: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/9.jpg)
Core DomainAggregats, Entités, Object Value...
![Page 10: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/10.jpg)
Application
![Page 11: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/11.jpg)
Presentation
![Page 12: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/12.jpg)
InfrastructurePersistance, framework
![Page 13: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/13.jpg)
Core Domain
![Page 14: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/14.jpg)
Aggregat
![Page 15: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/15.jpg)
Aggregat Question
Question - text - endDate
Identifier
Author
Options
![Page 16: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/16.jpg)
Racine de l’aggregat
![Page 17: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/17.jpg)
Entité
![Page 18: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/18.jpg)
Objets valeurs
![Page 19: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/19.jpg)
Apporter du sens
![Page 20: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/20.jpg)
Utiliser langage commun dans le modèle
● Utiliser des constructeurs nommés
● Tous les paramètres obligatoires sont dans le constructeur
● Utiliser des méthodes plutôt que des setters
● Rendre les classes du domaines finales
● Utiliser des factories
![Page 21: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/21.jpg)
QuestionRepository
final class Question
{
public static function ask(
QuestionIdentifier $id,
Author $author,
$text,
array $options,
\DateTimeImmutable $endDate
) {
}
private function __construct(...)
{
}
![Page 22: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/22.jpg)
Identifier les entités
![Page 23: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/23.jpg)
UUIDramsey/uuid
![Page 24: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/24.jpg)
Avec doctrine ?
● Utilisez le type embeddable pour les Object Value
● Utilisez le YAML/XML au lieu des annotations (dans la couche infrastructure)
● Attention aux jointures
● Les id en Object Value, c’est possible
![Page 25: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/25.jpg)
Repository
![Page 26: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/26.jpg)
QuestionRepository
<?php
namespace GBProd\ICantDecide\CoreDomain\Question;
interface QuestionRepository
{
public function find(QuestionIdentifier $id);
public function save(Question $question);
}
![Page 27: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/27.jpg)
Spécifications
![Page 28: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/28.jpg)
QuestionRepository
<?php
namespace GBProd\ICantDecide\CoreDomain\Question;
interface QuestionRepository
{
public function findSatisfying(Specification $spec);
}
![Page 29: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/29.jpg)
Une spécification
<?php
namespace GBProd\ICantDecide\CoreDomain\Specification\Question;
use GBProd\Specification\CompositeSpecification;
class IsAvailable extends CompositeSpecification
{
public function isSatisfiedBy($candidate)
{
return $candidate->getEndDate() > new \DateTime('now');
}
}
![Page 30: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/30.jpg)
Une spécification avec paramètres
<?php
namespace GBProd\ICantDecide\CoreDomain\Specification\Question;
use GBProd\Specification\CompositeSpecification;
class HasMoreVoteThan extends CompositeSpecification
{
public function __construct($minVote)
{
$this->minVotes = $minVotes;
}
public function isSatisfiedBy($candidate)
{
[...]
![Page 31: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/31.jpg)
Composition
<?php
$manyVotes = new HasMoreVoteThan(1000);
$available = new IsAvailable();
$byMe = new IsAskedBy(‘gbprod’);
$isPopular = $available
->andX($manyVotes)
->andX($byMe->not())
;
$isPopular>isSatifiedBy($question);
![Page 32: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/32.jpg)
Avec symfony ?kphoen/rulerz
happyr/doctrine-specification
![Page 33: DDD: Retour d'expérience](https://reader034.fdocuments.net/reader034/viewer/2022052405/58ed1aa61a28ab562f8b4697/html5/thumbnails/33.jpg)
Next ?