Download - DDD: Retour d'expérience
DDD: Retour d’expérience@GillesRoustan
Ma façon d’apprendre
I can’t decide
Pourquoi DDD ?
/** * @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
DDD to the rescue
4 couches
Core DomainAggregats, Entités, Object Value...
Application
Presentation
InfrastructurePersistance, framework
Core Domain
Aggregat
Aggregat Question
Question - text - endDate
Identifier
Author
Options
Racine de l’aggregat
Entité
Objets valeurs
Apporter du sens
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
QuestionRepository
final class Question
{
public static function ask(
QuestionIdentifier $id,
Author $author,
$text,
array $options,
\DateTimeImmutable $endDate
) {
}
private function __construct(...)
{
}
Identifier les entités
UUIDramsey/uuid
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
Repository
QuestionRepository
<?php
namespace GBProd\ICantDecide\CoreDomain\Question;
interface QuestionRepository
{
public function find(QuestionIdentifier $id);
public function save(Question $question);
}
Spécifications
QuestionRepository
<?php
namespace GBProd\ICantDecide\CoreDomain\Question;
interface QuestionRepository
{
public function findSatisfying(Specification $spec);
}
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');
}
}
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)
{
[...]
Composition
<?php
$manyVotes = new HasMoreVoteThan(1000);
$available = new IsAvailable();
$byMe = new IsAskedBy(‘gbprod’);
$isPopular = $available
->andX($manyVotes)
->andX($byMe->not())
;
$isPopular>isSatifiedBy($question);
Avec symfony ?kphoen/rulerz
happyr/doctrine-specification
Next ?