Diese Präsentation wurde erfolgreich gemeldet.
Die SlideShare-Präsentation wird heruntergeladen. ×

Biglietti, prego! A ticket for the (command) bus

Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Nächste SlideShare
$.Template
$.Template
Wird geladen in …3
×

Hier ansehen

1 von 245 Anzeige

Biglietti, prego! A ticket for the (command) bus

Herunterladen, um offline zu lesen

Slide del mio intervento al SymfonyDay 2017 di Milano
----
Ti è mai capitato di ritrovarti a scrivere controller enormi pieni di funzionalità? O che un cliente ti chiedesse di aggiungere un nuova funzionalità all’interno di un flusso già esistente? Hai mai sognato che esistesse un modo per scrivere codice realmente manutenibile, disaccopiato e semplice da far evolvere? Ti presento il Command Bus, un pattern che ti permette di scrivere codice altamente espressivo e manutenibile. In questo talk ti mostrerò i vantaggi dell’utilizzare il Command Bus (e anche l’Event Bus) con un esempio pratico in Symfony, usando la libreria SimpleBus.

Slide del mio intervento al SymfonyDay 2017 di Milano
----
Ti è mai capitato di ritrovarti a scrivere controller enormi pieni di funzionalità? O che un cliente ti chiedesse di aggiungere un nuova funzionalità all’interno di un flusso già esistente? Hai mai sognato che esistesse un modo per scrivere codice realmente manutenibile, disaccopiato e semplice da far evolvere? Ti presento il Command Bus, un pattern che ti permette di scrivere codice altamente espressivo e manutenibile. In questo talk ti mostrerò i vantaggi dell’utilizzare il Command Bus (e anche l’Event Bus) con un esempio pratico in Symfony, usando la libreria SimpleBus.

Anzeige
Anzeige

Weitere Verwandte Inhalte

Diashows für Sie (17)

Ähnlich wie Biglietti, prego! A ticket for the (command) bus (20)

Anzeige

Aktuellste (20)

Biglietti, prego! A ticket for the (command) bus

  1. 1. BIGLIETTI, PREGO!a ticket for the (command) bus
  2. 2. @magobaol UNA SERIE SCRITTA, SCENEGGIATA E NARRATA DA
  3. 3. INTERPRETI
  4. 4. GIULIA LA CLIENTE
  5. 5. ROBERTO IL PROGRAMMATORE
  6. 6. PROLOGO
  7. 7. Giulia è la direttrice generale di una compagnia di treni ad alta velocità
  8. 8. Giulia commissiona a Roberto un software per la vendita di biglietti online.
  9. 9. ORGANIZZAZIONE FILE s01e01
  10. 10. src !"" CustomerBundle !"" TicketBundle #   !"" Controller #   !"" DataFixtures #   !"" Entity #   !"" Form #   !"" Listener #   %"" Service | %"" Twig !"" TrainBundle %"" UserBundle TicketOffice Project
  11. 11. ENTITÀ s01e02
  12. 12. Ticket.php/** * @ORMEntity() * @ORMTable(name="ticket") */ class Ticket { /** * @ORMId * @ORMColumn(type="integer") * @ORMGeneratedValue(strategy="AUTO") **/ protected $id; /** * @ORMColumn(type="integer") **/ protected $customerId; /** * @ORMColumn(type="integer") **/ protected $trainNumber; /** * @ORMColumn(type="integer") **/ protected $seatNumber;
  13. 13. Ticket.php /** * @return mixed */ public function getId() { return $this->id; } /** * @return mixed */ public function getCustomerId() { return $this->customerId; } /** * @return mixed */ public function getTrainNumber() { return $this->trainNumber; }
  14. 14. Ticket.php /** * @param mixed $id */ public function setId($id) { $this->id = $id; } /** * @param mixed $customerId */ public function setCustomerId($customerId) { $this->customerId = $customerId; } /** * @param mixed $trainNumber */ public function setTrainNumber($trainNumber) { $this->trainNumber = $trainNumber; }
  15. 15. Ticket.php/** * @ORMEntity() * @ORMTable(name="ticket") */ class Ticket { /** * @ORMId * @ORMColumn(type="integer") * @ORMGeneratedValue(strategy="AUTO") **/ protected $id; /** * @ORMColumn(type="integer") **/ protected $customerId; /** * @ORMColumn(type="integer") **/ protected $trainNumber; /** * @ORMColumn(type="integer") **/ protected $seatNumber;
  16. 16. CONTROLLER s01e03
  17. 17. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) ); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); }
  18. 18. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) ); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); } $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) );
  19. 19. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) ); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); } $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber);
  20. 20. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) ); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); } $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush();
  21. 21. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat( $request->get('trainNumber'), $request->get(‘departure’) ); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); } return $this->render('thank_you_page.html.twig');
  22. 22. UN PICCOLO MIGLIORAMENTO s01e04
  23. 23. TicketController.php public function createTicketAction(Request $request) { //pick a seat... //create ticket... //save ticket... $customerRepo = $this->get('customer_repository'); $customer = $customerRepo->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmail($customer->getEmail(), $ticket); return $this->render('thank_you_page.html.twig'); }
  24. 24. UN PICCOLO PROBLEMA s01e05
  25. 25. TicketController.php public function createTicketAction(Request $request) { //pick a seat... $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate( $request->get('trainNumber'), $request->get('departure'), $seatNumber ); //create ticket... $ticket->setPNR($PNR); //save ticket... //retrieve customer... //send email to customer... return $this->render('thank_you_page.html.twig'); }
  26. 26. UN (ALTRO) PICCOLO MIGLIORAMENTO s01e06
  27. 27. TicketController.php public function createTicketAction(Request $request) { //pick a seat... //generate PNR... //create ticket... //save ticket... //retrieve customer... //send email to customer... $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  28. 28. L’INQUIETUDINE DI ROBERTO s01e07
  29. 29. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  30. 30. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  31. 31. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); return $this->render('thank_you_page.html.twig'); } s01e03
  32. 32. s01e04 public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer(...); return $this->render('thank_you_page.html.twig'); }
  33. 33. s01e05public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); return $this->render('thank_you_page.html.twig'); }
  34. 34. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); } s01e06
  35. 35. STORIA DI UN REFACTORING s01e08
  36. 36. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(...); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  37. 37. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(...); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  38. 38. DIETA!
  39. 39. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get(‘customerId') ); return $this->render('thank_you_page.html.twig'); }
  40. 40. TicketCreatorService.php class TicketCreatorService { public function __construct() { } }
  41. 41. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker ) { $this->seatNumberPicker = $seatNumberPicker; } }
  42. 42. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; } }
  43. 43. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; } }
  44. 44. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository, EntityRepository $ticketRepository ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; $this->ticketRepository = $ticketRepository; } }
  45. 45. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository, EntityRepository $ticketRepository, EntityManager $entityManager ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; $this->ticketRepository = $ticketRepository; $this->entityManager = $entityManager; } }
  46. 46. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository, EntityRepository $ticketRepository, EntityManager $entityManager, TicketMailer $ticketMailer ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; $this->ticketRepository = $ticketRepository; $this->entityManager = $entityManager; $this->ticketMailer = $ticketMailer; } }
  47. 47. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository, EntityRepository $ticketRepository, EntityManager $entityManager, TicketMailer $ticketMailer, PNRSMSSender $PNRSMSSender ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; $this->ticketRepository = $ticketRepository; $this->entityManager = $entityManager; $this->ticketMailer = $ticketMailer; $this->PNRSMSSender = $PNRSMSSender; } }
  48. 48. TicketCreatorService.php public function createTicket($trainNumber, $departure, $customerId) { $seatNumber = $this->seatNumberPicker->pickSeat(...); $PNR = $this->PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($customerId); $ticket->setTrainNumber($trainNumber); $ticket->setDeparture($departure); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $this->entityManager->persist($ticket); $this->entityManager->flush($ticket); $customer = $this->customerRepository->find($customerId); $this->ticketMailer->sendEmailToCustomer(...); $this->PNRSMSSender->send($customer->getMobile(), $PNR); }
  49. 49. meh…
  50. 50. public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(...); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); } TicketController.php
  51. 51. TicketController.php public function createTicketAction(Request $request) { $seatNumberPicker = $this->get('seat_number_picker'); $seatNumber = $seatNumberPicker->pickSeat(…); $PNRGenerator = $this->get('pnr_generator'); $PNR = $PNRGenerator->generate(…); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em = $this->get('doctrine.orm.entity_manager'); $em->persist($ticket); $em->flush(); $customerRepository = $this->get('customer_repository'); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer = $this->get('ticket_mailer'); $ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); } public function createTicketAction(Request $request) { $seatNumber = $seatNumberPicker->pickSeat(...); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em->persist($ticket); $em->flush(); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  52. 52. TicketController.php public function createTicketAction(Request $request) { $seatNumber = $seatNumberPicker->pickSeat(...); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em->persist($ticket); $em->flush(); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  53. 53. TicketController.php public function createTicketAction(Request $request) { $seatNumber = $seatNumberPicker->pickSeat(...); $PNR = $PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($request->get('customerId')); $ticket->setTrainNumber($request->get('trainNumber')); $ticket->setDeparture($request->get('departure')); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $em->persist($ticket); $em->flush(); $customer = $customerRepository->find($request->get('customerId')); $ticketMailer->sendEmailToCustomer(...); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  54. 54. TicketCreatorService.php public function createTicket($trainNumber, $departure, $customerId) { $seatNumber = $this->seatNumberPicker->pickSeat(...); $PNR = $this->PNRGenerator->generate(...); $ticket = new Ticket(); $ticket->setCustomerId($customerId); $ticket->setTrainNumber($trainNumber); $ticket->setDeparture($departure); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $this->entityManager->persist($ticket); $this->entityManager->flush($ticket); $customer = $this->customerRepository->find($customerId); $this->ticketMailer->sendEmailToCustomer(...); $this->PNRSMSSender->send($customer->getMobile(), $PNR); }
  55. 55. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get(‘customerId') ); return $this->render('thank_you_page.html.twig'); }
  56. 56. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get(‘customerId') ); return $this->render('thank_you_page.html.twig'); }
  57. 57. IL GRANDE SAGGIO s01e09
  58. 58. UNA PROMOZIONE!
  59. 59. TicketCreatorService.php class TicketCreatorService { public function createTicket($trainNumber, $departure, $customerId) { $seatNumber = $this->seatNumberPicker->pickSeat($trainNumber, $departure); $PNR = $this->PNRGenerator->generate($trainNumber, $departure, $seatNumber); $ticket = new Ticket(); $ticket->setCustomerId($customerId); $ticket->setTrainNumber($trainNumber); $ticket->setDeparture($departure); $ticket->setSeatNumber($seatNumber); $ticket->setPNR($PNR); $this->entityManager->persist($ticket); $this->entityManager->flush($ticket); $customer = $this->customerRepository->find($customerId); $this->ticketMailer->sendEmailToCustomer($customer->getEmail(), $ticket); $this->PNRSMSSender->send($customer->getMobile(), $PNR); } }
  60. 60. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get(‘customerId') ); return $this->render('thank_you_page.html.twig'); }
  61. 61. @agilegigi
  62. 62. Disclaimer: @agilegigi non ha mai detto questa cosa, ma secondo me potrebbe farlo.
  63. 63. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get('customerId')); $ticketRepository = $this->get('ticket_repository'); $ticketInADay = $ticketRepository->countTicketForADay($request->get('departure')); if ($ticketInADay == 100) { $winnerMailer = $this->get(‘winner_mailer'); $winnerMailer->send($customer); } return $this->render('thank_you_page.html.twig'); }
  64. 64. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get('customerId')); $ticketRepository = $this->get('ticket_repository'); $ticketInADay = $ticketRepository->countTicketForADay($request->get('departure')); if ($ticketInADay == 100) { $winnerMailer = $this->get(‘winner_mailer'); $winnerMailer->send($customer); } return $this->render('thank_you_page.html.twig'); } $ticketRepository = $this->get('ticket_repository'); $ticketInADay = $ticketRepository->countTicketForADay($request->get('departure')); if ($ticketInADay == 100) { $winnerMailer = $this->get(‘winner_mailer'); $winnerMailer->send($customer); }
  65. 65. e poi #lunedìcepensamo
  66. 66. PRIMA O POI C’È SEMPRE UN LUNEDì s01e10
  67. 67. il lunedì seguente…
  68. 68. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get('customerId')); $ticketRepository = $this->get('ticket_repository'); $ticketInADay = $ticketRepository->countTicketForADay($request->get('departure')); if ($ticketInADay == 100) { $winnerMailer = $this->get(‘winner_mailer'); $winnerMailer->send($customer); } return $this->render('thank_you_page.html.twig'); }
  69. 69. TicketController.php public function createTicketAction(Request $request) { $ticketCreator = $this->get('ticket_creator'); $ticketCreator->createTicket( $request->get('trainNumber'), $request->get('departure'), $request->get('customerId')); $ticketRepository = $this->get('ticket_repository'); $ticketInADay = $ticketRepository->countTicketForADay($request->get('departure')); if ($ticketInADay == 100) { $winnerMailer = $this->get(‘winner_mailer'); $winnerMailer->send($customer); } return $this->render('thank_you_page.html.twig'); }
  70. 70. FINE PRIMA STAGIONE
  71. 71. TRA LA PRIMA E LA SECONDA STAGIONE...
  72. 72. S.O.L.I.D.
  73. 73. DECOUPLING
  74. 74. DOMAIN DRIVEN DESIGN
  75. 75. UBIQUITOUS LANGUAGE
  76. 76. RICH MODEL
  77. 77. ONION ARCHITECTURE
  78. 78. in arrivo… STAGIONE 2
  79. 79. thanks to @leopro for the inspiring picture COMMAND BUS
  80. 80. EVENT BUS
  81. 81. È un canale di comunicazione che consente di inviare e ricevere messaggi in un'applicazione MESSAGE BUS
  82. 82. I messaggi possono essere comandi ed eventi MESSAGE BUS
  83. 83. COSA È UN COMANDO? È un messaggio tramite il quale si comunica al sistema che si vuole fare qualcosa [ request ] [ command ] [ command bus ] [ command handler ] (non restituisce nulla)
  84. 84. COSA È UN EVENTO? È un messaggio tramite il quale il sistema comunica che è accaduto qualcosa [ tipicamente in un command handler ] [ event ] [ event bus ] [ subscriber 1 ] [ subscriber 2 ]
  85. 85. TACTICIANhttps://tactician.thephpleague.com/
  86. 86. SIMPLEBUShttp://simplebus.github.io/MessageBus/
  87. 87. STAGIONE 2
  88. 88. ORGANIZZAZIONE FILE s02e01
  89. 89. . !"" app #   !"" Resources #   #   %"" views #   #   %"" default #   %"" config !"" bin !"" src #   %"" AppBundle #   %"" Controller !"" tests #   %"" AppBundle #   %"" Controller !"" var #   !"" cache #   !"" logs #   %"" sessions %"" web Symfony 3 folders
  90. 90. . !"" app #   !"" Resources #   #   %"" views #   #   %"" default #   %"" config !"" bin !"" src #   %"" AppBundle #   %"" Controller !"" tests #   %"" AppBundle #   %"" Controller !"" var #   !"" cache #   !"" logs #   %"" sessions %"" web Symfony 3 folders !"" src #   %"" AppBundle #   %"" Controller
  91. 91. src %"" AppBundle Domain folder
  92. 92. src !"" AppBundle %"" TicketOffice Domain folder
  93. 93. src !"" AppBundle %"" TicketOffice %"" Sale Domain folder
  94. 94. ENTITÀ s02e02
  95. 95. src !"" AppBundle %"" TicketOffice %"" Sale Domain folder
  96. 96. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity Domain folder
  97. 97. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity %"" Ticket.php Domain folder
  98. 98. Ticket.php class Ticket { private $id; private $customerId; private $trainNumber; private $departure; private $seatNumber; private $PNR; public function __construct( $id, $customerId, $trainNumber, $departure, $seatNumber, $PNR) { $this->id = $id; $this->customerId = $customerId; $this->trainNumber = $trainNumber; $this->departure = $departure; $this->seatNumber = $seatNumber; $this->PNR = $PNR; } }
  99. 99. Rectangle.php class Rectangle { private $width; private $height; public function getWidth() { return $this->width; } public function setWidth($width) { $this->width = $width; } public function getHeight() { return $this->height; } public function setHeight($height) { $this->height = $height; } }
  100. 100. Somewhere.php $rectangle = new Rectangle(); $rectangle->setWidth(100); $rectangle->setHeight(50);
  101. 101. Somewhere.php $rectangle = new Rectangle(); $rectangle->setWidth(100); $rectangle->setHeight(50); $rectangle = new Rectangle(); NON È UN RETTANGOLO!
  102. 102. Rectangle.php class Rectangle { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getWidth() { return $this->width; } public function getHeight() { return $this->height; } }
  103. 103. Somewhere.php $rectangle = new Rectangle(100, 50); QUESTO SÌ
  104. 104. Ticket.php class Ticket { private $id; private $customerId; private $trainNumber; private $departure; private $seatNumber; private $PNR; public function __construct( $id, $customerId, $trainNumber, $departure, $seatNumber, $PNR ) { $this->id = $id; $this->customerId = $customerId; $this->trainNumber = $trainNumber; $this->departure = $departure; $this->seatNumber = $seatNumber; $this->PNR = $PNR; } }
  105. 105. app !"" DoctrineMigrations !"" Resources %"" config %"" config.yml config.yml
  106. 106. config.yml doctrine: orm: auto_generate_proxy_classes: "%kernel.debug%" naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: false mappings: ticket_office.sale: type: yml dir: '%kernel.root_dir%/../src/AppBundle/ Resources/config/doctrine/Sale' is_bundle: false prefix: TicketOfficeSaleEntity alias: AppSale
  107. 107. config.yml doctrine: orm: auto_generate_proxy_classes: "%kernel.debug%" naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: false mappings: ticket_office.sale: type: yml dir: '%kernel.root_dir%/../src/AppBundle/ Resources/config/doctrine/Sale' is_bundle: false prefix: TicketOfficeSaleEntity alias: AppSale auto_mapping: false
  108. 108. config.yml doctrine: orm: auto_generate_proxy_classes: "%kernel.debug%" naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: false mappings: ticket_office.sale: type: yml dir: '%kernel.root_dir%/../src/AppBundle/ Resources/config/doctrine/Sale' is_bundle: false prefix: TicketOfficeSaleEntity alias: AppSale mappings: ticket_office.sale: type: yml dir: '%kernel.root_dir%/../src/AppBundle/ Resources/config/doctrine/Sale'
  109. 109. src !"" AppBundle #   !"" Controller #   !"" Entity #   %"" Resources #   %"" config #   %"" doctrine #   %"" Sale #   %"" Ticket.orm.yml %"" TicketOffice Ticket.orm.yml
  110. 110. Ticket.orm.yml TicketOfficeSaleEntityTicket: type: entity table: ticket id: id: type: string generator: { strategy: NONE } fields: customerId: type: integer trainNumber: type: integer departure: type: datetime seatNumber: type: integer PNR: type: string
  111. 111. TICKET REPOSITORY s02e03
  112. 112. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity !"" Ticket.php %"" TicketRepository.php TicketRepository.php
  113. 113. TicketRepository.php interface TicketRepository { public function save(Ticket $ticket); /** * @param $id * @return Ticket */ public function findById($id); }
  114. 114. src !"" AppBundle #   !"" Controller #   !"" Entity #   #   %"" DoctrineTicketRepository.php #   %"" Resources %"" TicketOffice DoctrineTicketRepository.php
  115. 115. DoctrineTicketRepository.php class DoctrineTicketRepository implements TicketRepository { private $entityRepository; private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; $this->entityRepository = $this->entityManager->getRepository('AppSale:Ticket'); } public function save(Ticket $ticket) { $this->entityManager->persist($ticket); $this->entityManager->flush(); } public function findById($id) { return $this->entityRepository->find($id); } }
  116. 116. DoctrineTicketRepository.php class DoctrineTicketRepository implements TicketRepository { private $entityRepository; private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; $this->entityRepository = $this->entityManager->getRepository('AppSale:Ticket'); } public function save(Ticket $ticket) { $this->entityManager->persist($ticket); $this->entityManager->flush(); } public function findById($id) { return $this->entityRepository->find($id); } } implements TicketRepository
  117. 117. DoctrineTicketRepository.php class DoctrineTicketRepository implements TicketRepository { private $entityRepository; private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; $this->entityRepository = $this->entityManager->getRepository('AppSale:Ticket'); } public function save(Ticket $ticket) { $this->entityManager->persist($ticket); $this->entityManager->flush(); } public function findById($id) { return $this->entityRepository->find($id); } }
  118. 118. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  119. 119. services: ticket_office.sale.entity.ticket_repository: class: AppBundleEntityDoctrineTicketRepository arguments: ["@doctrine.orm.default_entity_manager"] services.yml
  120. 120. SERVIZI s02e04
  121. 121. src !"" AppBundle %"" TicketOffice %"" Sale !"" Entity %"" Service %"" SeatPicker.php SeatPicker.php
  122. 122. class SeatPicker { public function pick($trainNumber, $departure) { return rand(1, 1000); } } SeatPicker.php
  123. 123. src !"" AppBundle %"" TicketOffice %"" Sale !"" Entity %"" Service !"" PNRGenerator.php %"" SeatPicker.php PNRGenerator.php
  124. 124. class PNRGenerator { public function generate( $trainNumber, $departure, $seatNumber ) { return uniqid(); } } PNRGenerator.php
  125. 125. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  126. 126. services: ticket_office.sale.seat_picker: class: TicketOfficeSaleServiceSeatPicker ticket_office.sale.pnr_generator: class: TicketOfficeSaleServicePNRGenerator services.yml
  127. 127. IL PRIMO COMANDO s02e05
  128. 128. SIMPLE BUS
  129. 129. composer require simple-bus/symfony-bridge @command_bus @event_bus
  130. 130. AppKernel.php public function registerBundles() { $bundles = [ //... new SimpleBusSymfonyBridgeSimpleBusCommandBusBundle(), new SimpleBusSymfonyBridgeSimpleBusEventBusBundle(), ]; //... }
  131. 131. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command #   %"" SellTicket.php !"" Entity %"" Service SellTicket.php
  132. 132. SellTicket.php class SellTicket { //private properties declaration... public function __construct( $id, $customerId, $trainNumber, $departure ) { $this->id = $id; $this->customerId = $customerId; $this->trainNumber = $trainNumber; $this->departure = $departure; } public function getCustomerId() {...} public function getTrainNumber() {...} public function getDeparture() {...} public function getId() {...} }
  133. 133. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command #   !"" SellTicket.php #   %"" SellTicketHandler.php !"" Entity %"" Service SellTicketHandler.php
  134. 134. SellTicketHandler.php class SellTicketHandler { //private properties declaration... public function __construct( SeatPicker $seatPicker, PNRGenerator $PNRGenerator, TicketRepository $ticketRepository ) { $this->seatPicker = $seatPicker; $this->PNRGenerator = $PNRGenerator; $this->ticketRepository = $ticketRepository; } }
  135. 135. SellTicketHandler.php class SellTicketHandler { //private properties declaration... public function __construct( SeatPicker $seatPicker, PNRGenerator $PNRGenerator, TicketRepository $ticketRepository ) { $this->seatPicker = $seatPicker; $this->PNRGenerator = $PNRGenerator; $this->ticketRepository = $ticketRepository; } } TicketRepository $ticketRepository
  136. 136. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); }
  137. 137. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); } public function handle(SellTicket $command)
  138. 138. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); } $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() );
  139. 139. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); } $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat );
  140. 140. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); } $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr );
  141. 141. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick( $command->getTrainNumber(), $command->getDeparture() ); $pnr = $this->PNRGenerator->generate( $command->getTrainNumber(), $command->getDeparture(), $seat ); $ticket = new Ticket( $command->getId(), $command->getCustomerId(), $command->getTrainNumber(), $command->getDeparture(), $seat, $pnr ); $this->ticketRepository->save($ticket); } $this->ticketRepository->save($ticket);
  142. 142. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  143. 143. services.yml services: ticket_office.commands.sell_ticket_handler: class: TicketOfficeSaleCommandSellTicketHandler arguments: - "@ticket_office.sale.seat_picker" - "@ticket_office.sale.pnr_generator" - "@ticket_office.sale.entity.ticket_repository" tags: - { name: command_handler, handles: TicketOfficeSaleCommandSellTicket }
  144. 144. services.yml services: ticket_office.commands.sell_ticket_handler: class: TicketOfficeSaleCommandSellTicketHandler arguments: - "@ticket_office.sale.seat_picker" - "@ticket_office.sale.pnr_generator" - "@ticket_office.sale.entity.ticket_repository" tags: - { name: command_handler, handles: TicketOfficeSaleCommandSellTicket } - "@ticket_office.sale.entity.ticket_repository"
  145. 145. services.yml services: ticket_office.commands.sell_ticket_handler: class: TicketOfficeSaleCommandSellTicketHandler arguments: - "@ticket_office.sale.seat_picker" - "@ticket_office.sale.pnr_generator" - "@ticket_office.sale.entity.ticket_repository" tags: - { name: command_handler, handles: TicketOfficeSaleCommandSellTicket } tags: - { name: command_handler, handles: TicketOfficeSaleCommandSellTicket }
  146. 146. CONTROLLER s02e06
  147. 147. src !"" AppBundle #   !"" Controller #   #   %"" TicketController.php #   !"" Entity #   %"" Resources %"" TicketOffice TicketController.php
  148. 148. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); }
  149. 149. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } $id = Uuid::uuid4();
  150. 150. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) );
  151. 151. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } $this->get('command_bus')->handle($command);
  152. 152. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 200); } return new JsonResponse(['id' => $id], 201);
  153. 153. CUSTOMER s02e07
  154. 154. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity # !"" Customer.php #   !"" Ticket.php #   %"" TicketRepository.php %"" Service Customer.php
  155. 155. Customer.php class Customer { private $id; private $email; public function __construct($id, $email) { $this->id = $id; $this->email = $email; } public function getId() {...} public function getEmail() {...} }
  156. 156. src !"" AppBundle #   !"" Controller #   !"" Entity #   %"" Resources #   %"" config #   %"" doctrine #   %"" Sale #   %"" Customer.orm.yml %"" TicketOffice Customer.orm.yml
  157. 157. Customer.orm.yml TicketOfficeSaleEntityCustomer: type: entity table: customer id: id: type: integer generator: { strategy: AUTO } fields: email: type: string
  158. 158. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity # !"" Customer.php #   !"" CustomerRepository.php #   !"" Ticket.php #   %"" TicketRepository.php %"" Service CustomerRepository.php
  159. 159. CustomerRepository.php interface CustomerRepository { /** * @param $id * @return Customer */ public function findById($id); }
  160. 160. src !"" AppBundle #   !"" Controller #   !"" Entity #   #   %"" DoctrineCustomerRepository.php #   %"" Resources %"" TicketOffice DoctrineCustomerRepository.php
  161. 161. DoctrineCustomerRepository.php class DoctrineCustomerRepository implements CustomerRepository { private $entityRepository; private $entityManager; public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; $this->entityRepository = $this->entityManager->getRepository('AppSale:Customer'); } public function findById($id) { return $this->entityRepository->find($id); } }
  162. 162. IL PRIMO EVENTO s02e08
  163. 163. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event #   %"" TicketSold.php %"" Service TicketSold.php
  164. 164. TicketSold.php class TicketSold { //private properties declarations... public function __construct($trainNumber, $customerId, $ticketId) { $this->trainNumber = $trainNumber; $this->customerId = $customerId; $this->ticketId = $ticketId; } public function getCustomerId() {...} public function getTicketId() {...} public function getTrainNumber() {...} }
  165. 165. SellTicketHandler.php class SellTicketHandler { //private properties declarations... public function __construct( SeatPicker $seatPicker, PNRGenerator $PNRGenerator, TicketRepository $ticketRepository, MessageBus $eventBus ) { $this->seatPicker = $seatPicker; $this->PNRGenerator = $PNRGenerator; $this->ticketRepository = $ticketRepository; $this->eventBus = $eventBus; } }
  166. 166. SellTicketHandler.php class SellTicketHandler { //private properties declarations... public function __construct( SeatPicker $seatPicker, PNRGenerator $PNRGenerator, TicketRepository $ticketRepository, MessageBus $eventBus ) { $this->seatPicker = $seatPicker; $this->PNRGenerator = $PNRGenerator; $this->ticketRepository = $ticketRepository; $this->eventBus = $eventBus; } } MessageBus $eventBus $this->eventBus = $eventBus;
  167. 167. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick(...); $pnr = $this->PNRGenerator->generate(...); $ticket = new Ticket(...); $this->ticketRepository->save($ticket); $event = new TicketSold( $command->getTrainNumber(), $command->getCustomerId(), $command->getId() ); $this->eventBus->handle($event); }
  168. 168. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick(...); $pnr = $this->PNRGenerator->generate(...); $ticket = new Ticket(...); $this->ticketRepository->save($ticket); $event = new TicketSold( $command->getTrainNumber(), $command->getCustomerId(), $command->getId() ); $this->eventBus->handle($event); } $event = new TicketSold( $command->getTrainNumber(), $command->getCustomerId(), $command->getId() );
  169. 169. SellTicketHandler.php public function handle(SellTicket $command) { $seat = $this->seatPicker->pick(...); $pnr = $this->PNRGenerator->generate(...); $ticket = new Ticket(...); $this->ticketRepository->save($ticket); $event = new TicketSold( $command->getTrainNumber(), $command->getCustomerId(), $command->getId() ); $this->eventBus->handle($event); } $this->eventBus->handle($event);
  170. 170. I SUBSCRIBER s02e09
  171. 171. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Service %"" Subscriber %"" SendEmailWhenTicketSold.php SendEmailWhenTicketSold.php
  172. 172. SendEmailWhenTicketSold.php class SendEmailWhenTicketSold { private $mailer; private $ticketRepository; private $customerRepository; public function __construct( Mailer $mailer, TicketRepository $ticketRepository, CustomerRepository $customerRepository ) { $this->mailer = $mailer; $this->ticketRepository = $ticketRepository; $this->customerRepository = $customerRepository; } public function notify(...) {...} }
  173. 173. SendEmailWhenTicketSold.php class SendEmailWhenTicketSold { private $mailer; private $ticketRepository; private $customerRepository; public function __construct( Mailer $mailer, TicketRepository $ticketRepository, CustomerRepository $customerRepository ) { $this->mailer = $mailer; $this->ticketRepository = $ticketRepository; $this->customerRepository = $customerRepository; } public function notify(...) {...} } TicketRepository $ticketRepository, CustomerRepository $customerRepository
  174. 174. SendEmailWhenTicketSold.php public function notify(TicketSold $event) { $customer = $this->customerRepository->findById( $event->getCustomerId() ); $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() ); $this->mailer->send($mailMessage); }
  175. 175. SendEmailWhenTicketSold.php public function notify(TicketSold $event) { $customer = $this->customerRepository->findById( $event->getCustomerId() ); $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() ); $this->mailer->send($mailMessage); } TicketSold $event
  176. 176. SendEmailWhenTicketSold.php public function notify(TicketSold $event) { $customer = $this->customerRepository->findById( $event->getCustomerId() ); $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() ); $this->mailer->send($mailMessage); } $customer = $this->customerRepository->findById( $event->getCustomerId() );
  177. 177. SendEmailWhenTicketSold.php public function notify(TicketSold $event) { $customer = $this->customerRepository->findById( $event->getCustomerId() ); $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() ); $this->mailer->send($mailMessage); } $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() );
  178. 178. SendEmailWhenTicketSold.php public function notify(TicketSold $event) { $customer = $this->customerRepository->findById( $event->getCustomerId() ); $mailMessage = $this->mailer->createMessage(); $mailMessage->setTo($customer->getEmail()); $mailMessage->setSubject('Dettagli biglietto'); $mailMessage->setFrom($this->sender_address); $mailMessage->setBody($body, sprintf( 'Hai acquistato il biglietto %s per il treno %s', $event->getTicketId(), $event->getTrainNumber() ); $this->mailer->send($mailMessage); } $this->mailer->send($mailMessage);
  179. 179. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  180. 180. services.yml ticket_office.sale.subscriber.send_email_when_ticket_sold: class: TicketOfficeSaleSubscriberSendEmailWhenTicketSold arguments: - "@mailer" - "@ticket_office.sale.entity.ticket_repository" - "@ticket_office.sale.entity.customer_repository" tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold }
  181. 181. services.yml ticket_office.sale.subscriber.send_email_when_ticket_sold: class: TicketOfficeSaleSubscriberSendEmailWhenTicketSold arguments: - "@mailer" - "@ticket_office.sale.entity.ticket_repository" - "@ticket_office.sale.entity.customer_repository" tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold } tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold }
  182. 182. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Service %"" Subscriber !"" SendEmailWhenTicketSold.php %"" SendPNRWhenTicketSold.php SendPNRWhenTicketSold.php
  183. 183. SendPNRWhenTicketSold.php class SendPNRWhenTicketSold { public function __construct( SMSGateway $SMSGateway, TicketRepository $ticketRepository, CustomerRepository $customerRepository ) { $this->SMSGateway = $SMSGateway; $this->ticketRepository = $ticketRepository; $this->customerRepository = $customerRepository; } public function notify(...) {...} }
  184. 184. SendPNRWhenTicketSold.php public function notify(TicketSold $event) { $ticket = $this->ticketRepository->findById( $event->getTicketId() ); $customer = $this->customerRepository->findById( $event->getCustomerId() ); $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) ); }
  185. 185. SendPNRWhenTicketSold.php public function notify(TicketSold $event) { $ticket = $this->ticketRepository->findById( $event->getTicketId() ); $customer = $this->customerRepository->findById( $event->getCustomerId() ); $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) ); } TicketSold $event
  186. 186. SendPNRWhenTicketSold.php public function notify(TicketSold $event) { $ticket = $this->ticketRepository->findById( $event->getTicketId() ); $customer = $this->customerRepository->findById( $event->getCustomerId() ); $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) ); } $ticket = $this->ticketRepository->findById( $event->getTicketId() );
  187. 187. SendPNRWhenTicketSold.php public function notify(TicketSold $event) { $ticket = $this->ticketRepository->findById( $event->getTicketId() ); $customer = $this->customerRepository->findById( $event->getCustomerId() ); $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) ); } $customer = $this->customerRepository->findById( $event->getCustomerId() );
  188. 188. SendPNRWhenTicketSold.php public function notify(TicketSold $event) { $ticket = $this->ticketRepository->findById( $event->getTicketId() ); $customer = $this->customerRepository->findById( $event->getCustomerId() ); $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) ); } $this->SMSGateway->send( $customer->getMobile(), sprintf('Il tuo PNR è %s', $ticket->getPNR()) );
  189. 189. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  190. 190. services.yml ticket_office.sale.subscriber.send_pnr_when_ticket_sold: class: TicketOfficeSaleSubscriberSendPNRWhenTicketSold arguments: - "@logger" - "@ticket_office.sale.entity.ticket_repository" - "@ticket_office.sale.entity.customer_repository" tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold }
  191. 191. services.yml ticket_office.sale.subscriber.send_pnr_when_ticket_sold: class: TicketOfficeSaleSubscriberSendPNRWhenTicketSold arguments: - "@logger" - "@ticket_office.sale.entity.ticket_repository" - "@ticket_office.sale.entity.customer_repository" tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold } tags: - { name: event_subscriber, subscribes_to: TicketOfficeSaleEventTicketSold }
  192. 192. ECCEZIONI s02e10
  193. 193. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); }
  194. 194. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } $this->get('command_bus')->handle($command);
  195. 195. TicketController.php /** * @Route("tickets/new", name="tickets.new") */ public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 200); } return new JsonResponse(['id' => $id], 201);
  196. 196. src !"" AppBundle !"" Basic #   %"" TicketOfficeException.php %"" TicketOffice TicketOfficeException.php
  197. 197. TicketOfficeException.php class TicketOfficeException extends Exception { public static function createWithDetails($details) { $e = new static(); $e->setDetails($details); return $e; } public function toArray() { $a['error']['code'] = $this->getCode(); $a['error']['message'] = $this->getMessage(); $a['error']['details'] = $this->getDetails(); return $a; } public function toJson() { return json_encode($this->toArray()); } public function setDetails($details) {...} public function getDetails() {...} }
  198. 198. src !"" AppBundle !"" Basic %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Exception #   %"" CustomerNotAllowedException.php !"" Service %"" Subscriber CustomerNotAllowedException.php
  199. 199. CustomerNotAllowedException.php class CustomerNotAllowedException extends TicketOfficeException { protected $code = 666; protected $message = 'Customer is not allowed to use this service.'; }
  200. 200. SellTicketHandler.php public function handle(SellTicket $command) { if ($command->getCustomerId() == 42) { throw new CustomerNotAllowedException(); } $seat = $this->seatPicker->pick(...); $pnr = $this->PNRGenerator->generate(...); $ticket = new Ticket(...); $this->ticketRepository->save($ticket); $event = new TicketSold(...); $this->eventBus->handle($event); }
  201. 201. SellTicketHandler.php public function handle(SellTicket $command) { if ($command->getCustomerId() == 42) { throw new CustomerNotAllowedException(); } $seat = $this->seatPicker->pick(...); $pnr = $this->PNRGenerator->generate(...); $ticket = new Ticket(...); $this->ticketRepository->save($ticket); $event = new TicketSold(...); $this->eventBus->handle($event); } if ($command->getCustomerId() == 42) { throw new CustomerNotAllowedException(); }
  202. 202. TicketController.php public function sellTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); try { $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } catch (CustomerNotAllowedException $e) { return new Response($e->toJson(), 400); } }
  203. 203. TicketController.php public function createTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); try { $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } catch (CustomerNotAllowedException $e) { return new Response($e->toJson(), 400); } } try { $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } catch (CustomerNotAllowedException $e) { return new Response($e->toJson(), 400); }
  204. 204. TicketController.php public function createTicketAction(Request $request) { $id = Uuid::uuid4(); $command = new SellTicket( $id, $request->get('customerId'), $request->get('trainNumber'), new DateTime($request->get('departure')) ); try { $this->get('command_bus')->handle($command); return new JsonResponse(['id' => $id], 201); } catch (CustomerNotAllowedException $e) { return new Response($e->toJson(), 400); } } try { } catch (CustomerNotAllowedException $e) { return new Response($e->toJson(), 400); }
  205. 205. FINE SECONDA STAGIONE
  206. 206. RIEPILOGO
  207. 207. COMMAND ‣Si usa per muovere il dominio ‣Va a buon fine per definizione ‣Non restituisce nessun risultato
  208. 208. COMMAND HANDLER ‣ Gestisce il comando ‣ Effettua validazioni di dominio ‣ Muove concretamente il dominio ‣ Solleva eccezioni in caso di problemi
  209. 209. EVENT ‣ Viene sollevato a fronte di qualcosa accaduto nel dominio (es: UserRegistered) ‣ Contiene informazioni arbitrarie
  210. 210. SUBSCRIBER ‣ Viene usato per eseguire azioni in risposta a degli eventi ‣ Non può muovere il dominio, al massimo inoltra la richiesta ad un comando ‣ Può operare autonomamente se la sua azione è destinata all'esterno del dominio
  211. 211. EPILOGO
  212. 212. Giulia ha continuato a chiedere nuove funzionalità perché un software non è mai finito
  213. 213. Roberto ha potuto implementarle senza scrivere controller o servizi di 2000 righe
  214. 214. I comandi, gli eventi e i subscriber sono piccoli e perciò facilmente testabili
  215. 215. Quindi Roberto ha potuto consegnare nuove funzionalità più velocemente e con meno errori
  216. 216. (anche) grazie al software di Roberto, l'azienda di Giulia prospera e vende biglietti
  217. 217. GRAZIE!
  218. 218. Francesco Face @magobaol
  219. 219. Francesco Face @magobaol Senior Developer
  220. 220. Francesco Face @magobaol Senior Developer
  221. 221. Approfondimenti A wave of command buses by Matthias Noback http://php-and-symfony.matthiasnoback.nl/2015/01/a-wave-of-command-buses/ SimpleBus Library
 http://simplebus.github.io/MessageBus/ Martin Eckardt Bachelor Thesis on CQRS and Event Sourcing https://drive.google.com/file/d/0B_enB2DMKeyzbF96VjdKdjIzOHc/view Bounded Context by Martin Fowler https://martinfowler.com/bliki/BoundedContext.html Ticket Office Sample Project https://github.com/magobaol/ticket-office

×