SlideShare a Scribd company logo
PHP7 e Rich Domain Model
Massimiliano Arione - @garakkio
SymfonyDay 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;
}
Mapping
class Giocatore {
/* @ORMManyToOne(targetEntity=”Squadra”) */
private $squadra;
/* @ORMColumn() */
private $nome;
/* @ORMColumn(type=”smallint”) */
private $numeroDiMaglia;
}
Validazione
class Giocatore {
/* @ORMManyToOne(targetEntity=”Squadra”)
@AssertNotNull() */
private $squadra;
/* @ORMColumn()
@AssertNotBlank() */
private $nome;
/* @ORMColumn(type=”smallint”)
@AssertNotNull()
@AssertType(type=”integer”)
@AssertRange(min=0,max=99 */
private $numeroDiMaglia;
}
Form
class 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,
]);
}
}
Setters
class 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
Form
squadra
nome
numeroDimaglia
Entity
squadra
nome
numeroDimaglia
Il vero problema
Form
squadra
nome
numeroDimaglia
Entity
squadra
nome
numeroDimaglia
table
squadra
nome
numero_di_maglia
DTO
class GiocatoreDTO {
public $squadra;
public $nome;
public $numeroDiMaglia;
}
Form
class 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,
]);
}
}
Action
public 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(),
]);
}
Action
public 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 Setters
class Giocatore {
public function __construct(Squadra $squadra, string $nome, int $numero) {
$this->squadra = $squadra;
$this->nome = $nome;
$this->numeroDiMaglia = $numero;
}
// ...
}
Action
public 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
NumeroDiMaglia
class 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 ValueObject
class Giocatore {
/* @ORMEmbedded(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
Configurazione
doctrine:
orm:
auto_mapping: false
mappings:
Entity:
type: xml
prefix: SportModelEntity
dir: "%kernel.root_dir%/config/doctrine/Entity"
is_bundle: false
ValueObject:
type: xml
prefix: SportModelValueObject
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!

More Related Content

PDF
Introduzione al Perl (BMR Genomics) - Lezione 1 Agosto 2014
ODP
Parlo al mio codice
PDF
E-commerce con SF: dal case study alla realtà
PDF
Introduce php7
PPTX
What’s New in PHP7?
PDF
Continuous Quality Assurance
PDF
PHP7: Hello World!
ODP
The promise of asynchronous PHP
Introduzione al Perl (BMR Genomics) - Lezione 1 Agosto 2014
Parlo al mio codice
E-commerce con SF: dal case study alla realtà
Introduce php7
What’s New in PHP7?
Continuous Quality Assurance
PHP7: Hello World!
The promise of asynchronous PHP

Viewers also liked (19)

PPTX
A new way to develop with WordPress!
PPTX
PHP7 Presentation
PDF
Migrating to php7
PDF
PHP7 is coming
PDF
reveal.js 3.0.0
PDF
Creating HTML Pages
PDF
Top Insights from SaaStr by Leading Enterprise Software Experts
PDF
Test Automation - Principles and Practices
PDF
CSS Grid Layout for Topconf, Linz
ODP
New Amazing Things about AngularJS 2.0
PDF
Node.js and The Internet of Things
PDF
The Future of Real-Time in Spark
PDF
Introduction to Information Architecture
PDF
Testing at Spotify
PDF
Launching a Rocketship Off Someone Else's Back
PDF
Nine Pages You Should Optimize on Your Blog and How
PPTX
Data made out of functions
PDF
Introduction to Development for the Internet
A new way to develop with WordPress!
PHP7 Presentation
Migrating to php7
PHP7 is coming
reveal.js 3.0.0
Creating HTML Pages
Top Insights from SaaStr by Leading Enterprise Software Experts
Test Automation - Principles and Practices
CSS Grid Layout for Topconf, Linz
New Amazing Things about AngularJS 2.0
Node.js and The Internet of Things
The Future of Real-Time in Spark
Introduction to Information Architecture
Testing at Spotify
Launching a Rocketship Off Someone Else's Back
Nine Pages You Should Optimize on Your Blog and How
Data made out of functions
Introduction to Development for the Internet
Ad

More from Massimiliano Arione (20)

PDF
Typed models pug roma febbraio 2020
PPTX
Pipelines!
PDF
Il nostro amico Stan
PDF
PSR7 - interoperabilità HTTP
PDF
Disinstallare fos user bundle e vivere felici
PDF
MAGA - PUG Roma giugno 2017
PDF
PHP on the desktop
PDF
Scrivere e leggere log con elastic
PDF
The metrics
PDF
Managing frontend libs in your Symfony project
PDF
Translating symfony docs
PDF
Managing frontend libs in your php project
PDF
Gestire librerie di frontend in php
PDF
PHP, non lo stesso vecchio linguaggio
PDF
Gestione delle dipendenze con Composer
PDF
Migrare da symfony 1 a Symfony2
PDF
Case study OmniAuto.it
ODP
Symfony: un framework per il web
PPT
Paypal + symfony
ODP
Sviluppo rapido di applicazioni con PHP
Typed models pug roma febbraio 2020
Pipelines!
Il nostro amico Stan
PSR7 - interoperabilità HTTP
Disinstallare fos user bundle e vivere felici
MAGA - PUG Roma giugno 2017
PHP on the desktop
Scrivere e leggere log con elastic
The metrics
Managing frontend libs in your Symfony project
Translating symfony docs
Managing frontend libs in your php project
Gestire librerie di frontend in php
PHP, non lo stesso vecchio linguaggio
Gestione delle dipendenze con Composer
Migrare da symfony 1 a Symfony2
Case study OmniAuto.it
Symfony: un framework per il web
Paypal + symfony
Sviluppo rapido di applicazioni con PHP
Ad

PHP7 e Rich Domain Model

  • 1. PHP7 e Rich Domain Model Massimiliano Arione - @garakkio SymfonyDay 2016
  • 3. PHP 7 e Rich Domain Model
  • 4. PHP 7 e Rich Domain Model Come sono arrivato a Rich Domain Model passando per PHP 7
  • 5. Non parleremo di... ● quanto è figo PHP 7
  • 6. Non parleremo di... ● quanto è figo PHP 7 ● teoria del Rich Domain Model, pattern e anti-pattern, ecc.
  • 7. Non parleremo di... ● quanto è figo PHP 7 ● teoria del Rich Domain Model, pattern e anti-pattern, ecc.
  • 8. C’era una volta Una classica applicazione Symfony...
  • 9. Entità class Giocatore { private $squadra; private $nome; private $numeroDiMaglia; }
  • 10. Mapping class Giocatore { /* @ORMManyToOne(targetEntity=”Squadra”) */ private $squadra; /* @ORMColumn() */ private $nome; /* @ORMColumn(type=”smallint”) */ private $numeroDiMaglia; }
  • 11. Validazione class Giocatore { /* @ORMManyToOne(targetEntity=”Squadra”) @AssertNotNull() */ private $squadra; /* @ORMColumn() @AssertNotBlank() */ private $nome; /* @ORMColumn(type=”smallint”) @AssertNotNull() @AssertType(type=”integer”) @AssertRange(min=0,max=99 */ private $numeroDiMaglia; }
  • 12. Form class 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, ]); } }
  • 13. Setters class Giocatore { public function setSquadra(Squadra $squadra) { $this->squadra = $squadra; } public function setNome($nome) { $this->nome = $nome; } public function setNumeroDiMaglia($numero) { $this->numeroDiMaglia = $numero; } // ... }
  • 14. Getters class Giocatore { // ... public function getSquadra() { return $this->squadra; } public function getNome() { return $this->nome; } public function getNumeroDiMaglia() { return $this->numeroDiMaglia; } }
  • 16. Here comes PHP 7... <=>
  • 17. Here comes PHP 7... ● scalar type hints ● return types
  • 18. 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; } // ... }
  • 19. 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; } }
  • 20. Oops! Fatal error: Uncaught TypeError: Argument 1 passed to setNome() must be an instance of string, null given
  • 21. 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
  • 24. DTO class GiocatoreDTO { public $squadra; public $nome; public $numeroDiMaglia; }
  • 25. Form class 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, ]); } }
  • 26. Action public 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(), ]); }
  • 27. Action public 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(), ]); }
  • 28. No more Setters class Giocatore { public function __construct(Squadra $squadra, string $nome, int $numero) { $this->squadra = $squadra; $this->nome = $nome; $this->numeroDiMaglia = $numero; } // ... }
  • 29. Action public 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(), ]); }
  • 31. ValueObject Un oggetto definito dai suoi attributi
  • 32. ValueObject Un oggetto definito dai suoi attributi Due ValueObject sono uguali se i loro attributi sono uguali
  • 33. NumeroDiMaglia class 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; } }
  • 34. Entity con ValueObject class Giocatore { /* @ORMEmbedded(class=”NumeroDimaglia”) */ private $numeroDiMaglia; public function __construct( Squadra $squadra, string $nome, NumeroDiMaglia $numero ) { $this->squadra = $squadra; $this->nome = $nome; $this->numeroDiMaglia = $numero; } // ... }
  • 36. Vantaggi ● tipo più esplicito ● vincoli interni al dominio
  • 37. Vantaggi ● tipo più esplicito ● vincoli interni al dominio ● entità meno appiattita sulla persistenza
  • 38. Vantaggi ● tipo più esplicito ● vincoli interni al dominio ● entità meno appiattita sulla persistenza ● entità meno accoppiata al framework
  • 39. Configurazione doctrine: orm: auto_mapping: false mappings: Entity: type: xml prefix: SportModelEntity dir: "%kernel.root_dir%/config/doctrine/Entity" is_bundle: false ValueObject: type: xml prefix: SportModelValueObject dir: "%kernel.root_dir%/config/doctrine/ValueObject" is_bundle: false
  • 41. What’s next? ● implementare CQRS con CommandBus
  • 42. What’s next? ● implementare CQRS con CommandBus ● plus: i Command sono già pronti (sono i nostri DTO)