php7 e rich domain model

43
PHP7 e Rich Domain Model Massimiliano Arione - @garakkio SymfonyDay 2016

Upload: massimiliano-arione

Post on 15-Apr-2017

392 views

Category:

Technology


0 download

TRANSCRIPT

PHP7 e Rich Domain Model

Massimiliano Arione - @garakkioSymfonyDay 2016

about

PHP 7 e Rich Domain Model

PHP 7 e Rich Domain Model

Come sono arrivato a Rich Domain Model passando per PHP 7

Non parleremo di...

● quanto è figo PHP 7

Non parleremo di...

● quanto è figo PHP 7● teoria del Rich Domain Model, pattern e anti-pattern, ecc.

Non parleremo di...

● quanto è figo PHP 7● teoria del Rich Domain Model, pattern e anti-pattern, ecc.

C’era una volta

Una classica applicazione Symfony...

Entitàclass Giocatore { private $squadra; private $nome; private $numeroDiMaglia;}

Mappingclass Giocatore { /* @ORM\ManyToOne(targetEntity=”Squadra”) */ private $squadra; /* @ORM\Column() */ private $nome; /* @ORM\Column(type=”smallint”) */ private $numeroDiMaglia;}

Validazioneclass Giocatore { /* @ORM\ManyToOne(targetEntity=”Squadra”) @AssertNotNull() */ private $squadra; /* @ORM\Column() @Assert\NotBlank() */ private $nome; /* @ORM\Column(type=”smallint”) @Assert\NotNull() @Assert\Type(type=”integer”) @Assert\Range(min=0,max=99 */ private $numeroDiMaglia;}

Formclass GiocatoreType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {

$builder

->add('squadra')

->add('nome')

->add('numeroDiMaglia', IntegerType::class)

;

}

public function configureOptions(OptionsResolver $resolver) {

$resolver->setDefaults([

'data_class' => Giocatore::class,

]);

}

}

Settersclass Giocatore {

public function setSquadra(Squadra $squadra) {

$this->squadra = $squadra;

}

public function setNome($nome) {

$this->nome = $nome;

}

public function setNumeroDiMaglia($numero) {

$this->numeroDiMaglia = $numero;

}

// ...

}

Getters

class Giocatore {

// ...

public function getSquadra() {

return $this->squadra;

}

public function getNome() {

return $this->nome;

}

public function getNumeroDiMaglia() {

return $this->numeroDiMaglia;

}

}

Here comes PHP 7...

Here comes PHP 7...

<=>

Here comes PHP 7...

● scalar type hints● return types

Setters (tipizzati)class Giocatore {

public function setSquadra(Squadra $squadra) {

$this->squadra = $squadra;

}

public function setNome(string $nome) {

$this->nome = $nome;

}

public function setNumeroDiMaglia(int $numero) {

$this->numeroDiMaglia = $numero;

}

// ...

}

Getters (tipizzati)class Giocatore {

// ...

public function getSquadra(): Squadra {

return $this->squadra;

}

public function getNome(): string {

return $this->nome;

}

public function getNumeroDiMaglia(): int {

return $this->numeroDiMaglia;

}

}

Oops!

Fatal error: Uncaught TypeError: Argument 1 passed to setNome() must be an instance of string, null given

Oops2!

Fatal error: Uncaught TypeError: Argument 1 passed to setNome() must be an instance of string, null given

Fatal error: Uncaught TypeError: Return value of getNome() must be an instance of string, null returned

Il vero problema

Formsquadra

nomenumeroDimaglia

Entitysquadra

nomenumeroDimaglia

Il vero problema

Formsquadra

nomenumeroDimaglia

Entitysquadra

nomenumeroDimaglia

tablesquadra

nomenumero_di_maglia

DTOclass GiocatoreDTO { public $squadra; public $nome; public $numeroDiMaglia;}

Formclass GiocatoreType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {

$builder

->add('squadra')

->add('nome')

->add('numeroDiMaglia', IntegerType::class)

;

}

public function configureOptions(OptionsResolver $resolver) {

$resolver->setDefaults([

'data_class' => GiocatoreDTO::class,

]);

}

}

Actionpublic function nuovoAction(Request $request) {

$giocatore = new Giocatore();

$form = $this->createForm(GiocatoreType::class, $giocatore);

if ($form->handleRequest($request)->isValid()) {

$this->getDoctrine()->getManager()->persist($giocatore);

$this->getDoctrine()->getManager()->flush();

return $this->redirectToRoute('giocatori');

}

return $this->render('giocatore/nuovo.html.twig', [

'form' => $form->createView(),

]);

}

Actionpublic function nuovoAction(Request $request)

{

$dto = new GiocatoreDTO();

$form = $this->createForm(GiocatoreType::class, $dto);

if ($form->handleRequest($request)->isValid()) {

$giocatore = new Giocatore();

// ...

$this->getDoctrine()->getManager()->persist($giocatore);

$this->getDoctrine()->getManager()->flush();

return $this->redirectToRoute('giocatori');

}

return $this->render('giocatore/nuovo.html.twig', [

'form' => $form->createView(),

]);

}

No more Settersclass Giocatore {

public function __construct(Squadra $squadra, string $nome, int $numero) {

$this->squadra = $squadra;

$this->nome = $nome;

$this->numeroDiMaglia = $numero;

}

// ...

}

Actionpublic function nuovoAction(Request $request) {

$dto = new GiocatoreDTO();

$form = $this->createForm(GiocatoreType::class, $dto);

if ($form->handleRequest($request)->isValid()) {

$giocatore = new Giocatore($dto->squadra, $dto->nome, $dto->numeroDiMaglia);

$this->getDoctrine()->getManager()->persist($giocatore);

$this->getDoctrine()->getManager()->flush();

return $this->redirectToRoute('giocatori');

}

return $this->render('giocatore/nuovo.html.twig', [

'form' => $form->createView(),

]);

}

ValueObject

ValueObject

Un oggetto definito dai suoi attributi

ValueObject

Un oggetto definito dai suoi attributi

Due ValueObject sono uguali se i loro attributi sono uguali

NumeroDiMagliaclass NumeroDimaglia() {

private $numero;

public function __construct(int $numero) {

if ($numero < 0 || $numero > 99) {

throw new \DomainException('Il numero deve esere tra 0 e 99');

}

$this->numero = $numero;

}

public function __toString() {

return (string) $this->numero;

}

}

Entity con ValueObjectclass Giocatore {

/* @ORM\Embedded(class=”NumeroDimaglia”) */

private $numeroDiMaglia;

public function __construct(

Squadra $squadra,

string $nome,

NumeroDiMaglia $numero

) {

$this->squadra = $squadra;

$this->nome = $nome;

$this->numeroDiMaglia = $numero;

}

// ...

}

Vantaggi

● tipo più esplicito

Vantaggi

● tipo più esplicito● vincoli interni al dominio

Vantaggi

● tipo più esplicito● vincoli interni al dominio● entità meno appiattita sulla persistenza

Vantaggi

● tipo più esplicito● vincoli interni al dominio● entità meno appiattita sulla persistenza● entità meno accoppiata al framework

Configurazionedoctrine:

orm:

auto_mapping: false

mappings:

Entity:

type: xml

prefix: Sport\Model\Entity

dir: "%kernel.root_dir%/config/doctrine/Entity"

is_bundle: false

ValueObject:

type: xml

prefix: Sport\Model\ValueObject

dir: "%kernel.root_dir%/config/doctrine/ValueObject"

is_bundle: false

What’s next?

What’s next?

● implementare CQRS con CommandBus

What’s next?

● implementare CQRS con CommandBus● plus: i Command sono già pronti (sono i nostri DTO)

That’s all!