SlideShare a Scribd company logo
BIGLIETTI, PREGO!a ticket for the (command) bus
@magobaol
UNA SERIE SCRITTA, SCENEGGIATA E NARRATA DA
INTERPRETI
GIULIA
LA CLIENTE
ROBERTO
IL PROGRAMMATORE
PROLOGO
Giulia è la direttrice
generale di una
compagnia di treni
ad alta velocità
Giulia commissiona a
Roberto un software
per la vendita di
biglietti online.
ORGANIZZAZIONE FILE
s01e01
src
!"" CustomerBundle
!"" TicketBundle
#   !"" Controller
#   !"" DataFixtures
#   !"" Entity
#   !"" Form
#   !"" Listener
#   %"" Service
| %"" Twig
!"" TrainBundle
%"" UserBundle
TicketOffice Project
ENTITÀ
s01e02
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;
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;
}
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;
}
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;
CONTROLLER
s01e03
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');
}
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’)
);
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);
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();
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');
UN PICCOLO
MIGLIORAMENTO
s01e04
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');
}
UN PICCOLO PROBLEMA
s01e05
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');
}
UN (ALTRO) PICCOLO 

MIGLIORAMENTO
s01e06
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');
}
L’INQUIETUDINE DI
ROBERTO
s01e07
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)
{
$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)
{
$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
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');
}
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');
}
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
STORIA DI UN
REFACTORING
s01e08
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');
}
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');
}
DIETA!
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');
}
TicketCreatorService.php
class TicketCreatorService
{
public function __construct()
{
}
}
TicketCreatorService.php
class TicketCreatorService
{
public function __construct(
SeatNumberPicker $seatNumberPicker
) {
$this->seatNumberPicker = $seatNumberPicker;
}
}
TicketCreatorService.php
class TicketCreatorService
{
public function __construct(
SeatNumberPicker $seatNumberPicker,
PNRGenerator $PNRGenerator
) {
$this->seatNumberPicker = $seatNumberPicker;
$this->PNRGenerator = $PNRGenerator;
}
}
TicketCreatorService.php
class TicketCreatorService
{
public function __construct(
SeatNumberPicker $seatNumberPicker,
PNRGenerator $PNRGenerator,
EntityRepository $customerRepository
) {
$this->seatNumberPicker = $seatNumberPicker;
$this->PNRGenerator = $PNRGenerator;
$this->customerRepository = $customerRepository;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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);
}
meh…
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
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');
}
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');
}
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');
}
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);
}
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');
}
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');
}
IL GRANDE SAGGIO
s01e09
UNA PROMOZIONE!
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);
}
}
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');
}
@agilegigi
Disclaimer: @agilegigi non ha mai detto questa
cosa, ma secondo me potrebbe farlo.
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');
}
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);
}
e poi #lunedìcepensamo
PRIMA O POI 

C’È SEMPRE UN LUNEDì
s01e10
il lunedì seguente…
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');
}
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');
}
FINE PRIMA STAGIONE
TRA LA PRIMA E LA
SECONDA STAGIONE...
S.O.L.I.D.
DECOUPLING
DOMAIN DRIVEN DESIGN
UBIQUITOUS LANGUAGE
RICH MODEL
ONION ARCHITECTURE
in arrivo…
STAGIONE 2
thanks to @leopro for the inspiring picture
COMMAND BUS
EVENT BUS
È un canale di comunicazione che
consente di inviare e ricevere messaggi in
un'applicazione
MESSAGE BUS
I messaggi possono essere
comandi ed eventi
MESSAGE BUS
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)
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 ]
TACTICIANhttps://tactician.thephpleague.com/
SIMPLEBUShttp://simplebus.github.io/MessageBus/
STAGIONE 2
ORGANIZZAZIONE FILE
s02e01
.
!"" app
#   !"" Resources
#   #   %"" views
#   #   %"" default
#   %"" config
!"" bin
!"" src
#   %"" AppBundle
#   %"" Controller
!"" tests
#   %"" AppBundle
#   %"" Controller
!"" var
#   !"" cache
#   !"" logs
#   %"" sessions
%"" web
Symfony 3 folders
.
!"" app
#   !"" Resources
#   #   %"" views
#   #   %"" default
#   %"" config
!"" bin
!"" src
#   %"" AppBundle
#   %"" Controller
!"" tests
#   %"" AppBundle
#   %"" Controller
!"" var
#   !"" cache
#   !"" logs
#   %"" sessions
%"" web
Symfony 3 folders
!"" src
#   %"" AppBundle
#   %"" Controller
src
%"" AppBundle
Domain folder
src
!"" AppBundle
%"" TicketOffice
Domain folder
src
!"" AppBundle
%"" TicketOffice
%"" Sale
Domain folder
ENTITÀ
s02e02
src
!"" AppBundle
%"" TicketOffice
%"" Sale
Domain folder
src
!"" AppBundle
%"" TicketOffice
%"" Sale
%"" Entity
Domain folder
src
!"" AppBundle
%"" TicketOffice
%"" Sale
%"" Entity
%"" Ticket.php
Domain folder
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;
}
}
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;
}
}
Somewhere.php
$rectangle = new Rectangle();
$rectangle->setWidth(100);
$rectangle->setHeight(50);
Somewhere.php
$rectangle = new Rectangle();
$rectangle->setWidth(100);
$rectangle->setHeight(50);
$rectangle = new Rectangle();
NON È UN RETTANGOLO!
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;
}
}
Somewhere.php
$rectangle = new Rectangle(100, 50);
QUESTO SÌ
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;
}
}
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" config.yml
config.yml
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
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
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'
src
!"" AppBundle
#   !"" Controller
#   !"" Entity
#   %"" Resources
#   %"" config
#   %"" doctrine
#   %"" Sale
#   %"" Ticket.orm.yml
%"" TicketOffice
Ticket.orm.yml
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
TICKET REPOSITORY
s02e03
src
!"" AppBundle
%"" TicketOffice
%"" Sale
%"" Entity
!"" Ticket.php
%"" TicketRepository.php
TicketRepository.php
TicketRepository.php
interface TicketRepository
{
public function save(Ticket $ticket);
/**
* @param $id
* @return Ticket
*/
public function findById($id);
}
src
!"" AppBundle
#   !"" Controller
#   !"" Entity
#   #   %"" DoctrineTicketRepository.php
#   %"" Resources
%"" TicketOffice
DoctrineTicketRepository.php
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);
}
}
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
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);
}
}
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" services.yml
services.yml
services:
ticket_office.sale.entity.ticket_repository:
class: AppBundleEntityDoctrineTicketRepository
arguments: ["@doctrine.orm.default_entity_manager"]
services.yml
SERVIZI
s02e04
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Entity
%"" Service
%"" SeatPicker.php
SeatPicker.php
class SeatPicker
{
public function pick($trainNumber, $departure)
{
return rand(1, 1000);
}
}
SeatPicker.php
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Entity
%"" Service
!"" PNRGenerator.php
%"" SeatPicker.php
PNRGenerator.php
class PNRGenerator
{
public function generate(
$trainNumber,
$departure,
$seatNumber
) {
return uniqid();
}
}
PNRGenerator.php
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" services.yml
services.yml
services:
ticket_office.sale.seat_picker:
class: TicketOfficeSaleServiceSeatPicker
ticket_office.sale.pnr_generator:
class: TicketOfficeSaleServicePNRGenerator
services.yml
IL PRIMO COMANDO
s02e05
SIMPLE BUS
composer require simple-bus/symfony-bridge
@command_bus
@event_bus
AppKernel.php
public function registerBundles()
{
$bundles = [
//...
new SimpleBusSymfonyBridgeSimpleBusCommandBusBundle(),
new SimpleBusSymfonyBridgeSimpleBusEventBusBundle(),
];
//...
}
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
#   %"" SellTicket.php
!"" Entity
%"" Service
SellTicket.php
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() {...}
}
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
#   !"" SellTicket.php
#   %"" SellTicketHandler.php
!"" Entity
%"" Service
SellTicketHandler.php
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;
}
}
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
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);
}
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)
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()
);
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
);
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
);
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);
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" services.yml
services.yml
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 }
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"
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 }
CONTROLLER
s02e06
src
!"" AppBundle
#   !"" Controller
#   #   %"" TicketController.php
#   !"" Entity
#   %"" Resources
%"" TicketOffice
TicketController.php
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);
}
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();
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'))
);
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);
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);
CUSTOMER
s02e07
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
# !"" Customer.php
#   !"" Ticket.php
#   %"" TicketRepository.php
%"" Service
Customer.php
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() {...}
}
src
!"" AppBundle
#   !"" Controller
#   !"" Entity
#   %"" Resources
#   %"" config
#   %"" doctrine
#   %"" Sale
#   %"" Customer.orm.yml
%"" TicketOffice
Customer.orm.yml
Customer.orm.yml
TicketOfficeSaleEntityCustomer:
type: entity
table: customer
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
email:
type: string
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
# !"" Customer.php
#   !"" CustomerRepository.php
#   !"" Ticket.php
#   %"" TicketRepository.php
%"" Service
CustomerRepository.php
CustomerRepository.php
interface CustomerRepository
{
/**
* @param $id
* @return Customer
*/
public function findById($id);
}
src
!"" AppBundle
#   !"" Controller
#   !"" Entity
#   #   %"" DoctrineCustomerRepository.php
#   %"" Resources
%"" TicketOffice
DoctrineCustomerRepository.php
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);
}
}
IL PRIMO EVENTO
s02e08
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
!"" Event
#   %"" TicketSold.php
%"" Service
TicketSold.php
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() {...}
}
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;
}
}
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;
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);
}
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()
);
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);
I SUBSCRIBER
s02e09
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
!"" Event
!"" Service
%"" Subscriber
%"" SendEmailWhenTicketSold.php
SendEmailWhenTicketSold.php
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(...) {...}
}
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
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);
}
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
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()
);
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()
);
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);
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" services.yml
services.yml
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 }
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 }
src
!"" AppBundle
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
!"" Event
!"" Service
%"" Subscriber
!"" SendEmailWhenTicketSold.php
%"" SendPNRWhenTicketSold.php
SendPNRWhenTicketSold.php
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(...) {...}
}
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())
);
}
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
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()
);
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()
);
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())
);
app
!"" DoctrineMigrations
!"" Resources
%"" config
%"" services.yml
services.yml
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 }
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 }
ECCEZIONI
s02e10
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);
}
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);
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);
src
!"" AppBundle
!"" Basic
#   %"" TicketOfficeException.php
%"" TicketOffice
TicketOfficeException.php
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() {...}
}
src
!"" AppBundle
!"" Basic
%"" TicketOffice
%"" Sale
!"" Command
!"" Entity
!"" Event
!"" Exception
#   %"" CustomerNotAllowedException.php
!"" Service
%"" Subscriber
CustomerNotAllowedException.php
CustomerNotAllowedException.php
class CustomerNotAllowedException extends TicketOfficeException
{
protected $code = 666;
protected $message =
'Customer is not allowed to use this service.';
}
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);
}
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();
}
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);
}
}
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);
}
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);
}
FINE SECONDA STAGIONE
RIEPILOGO
COMMAND
‣Si usa per muovere il dominio
‣Va a buon fine per definizione
‣Non restituisce nessun risultato
COMMAND HANDLER
‣ Gestisce il comando
‣ Effettua validazioni di dominio
‣ Muove concretamente il dominio
‣ Solleva eccezioni in caso di problemi
EVENT
‣ Viene sollevato a fronte di qualcosa accaduto nel
dominio (es: UserRegistered)
‣ Contiene informazioni arbitrarie
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
EPILOGO
Giulia ha continuato
a chiedere nuove
funzionalità perché
un software non è
mai finito
Roberto ha potuto
implementarle
senza scrivere
controller o servizi
di 2000 righe
I comandi, gli eventi
e i subscriber sono
piccoli e perciò
facilmente testabili
Quindi Roberto ha
potuto consegnare
nuove funzionalità
più velocemente e
con meno errori
(anche) grazie al
software di
Roberto, l'azienda
di Giulia prospera e
vende biglietti
GRAZIE!
Francesco Face
@magobaol
Francesco Face
@magobaol
Senior Developer
Francesco Face
@magobaol
Senior Developer
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

More Related Content

What's hot

Keeping It Simple
Keeping It SimpleKeeping It Simple
Keeping It Simple
Stephanie Leary
 
Cube-S : Storytelling and Panchatantra
Cube-S : Storytelling and PanchatantraCube-S : Storytelling and Panchatantra
Cube-S : Storytelling and Panchatantra
Vikas Kumar
 
Everything you always wanted to know about forms* *but were afraid to ask
Everything you always wanted to know about forms* *but were afraid to askEverything you always wanted to know about forms* *but were afraid to ask
Everything you always wanted to know about forms* *but were afraid to ask
Andrea Giuliano
 
Symfony day 2016
Symfony day 2016Symfony day 2016
Symfony day 2016
Samuele Lilli
 
Devs for Leokz e 7Masters - WTF Oriented Programming
Devs for Leokz e 7Masters - WTF Oriented ProgrammingDevs for Leokz e 7Masters - WTF Oriented Programming
Devs for Leokz e 7Masters - WTF Oriented Programming
Fabio Akita
 
Leveraging Symfony2 Forms
Leveraging Symfony2 FormsLeveraging Symfony2 Forms
Leveraging Symfony2 Forms
Bernhard Schussek
 
Codigo taller-plugins
Codigo taller-pluginsCodigo taller-plugins
Codigo taller-plugins
Rocío Valdivia
 
Quality code by design
Quality code by designQuality code by design
Quality code by design
WP Developers Club
 
How to count money using PHP and not lose money
How to count money using PHP and not lose moneyHow to count money using PHP and not lose money
How to count money using PHP and not lose money
Piotr Horzycki
 
SQL Injection Part 2
SQL Injection Part 2SQL Injection Part 2
SQL Injection Part 2
n|u - The Open Security Community
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
Massimiliano Arione
 
Pervasive 2011 Talk on Situated Glyphs
Pervasive 2011 Talk on Situated GlyphsPervasive 2011 Talk on Situated Glyphs
Pervasive 2011 Talk on Situated Glyphs
Fahim Kawsar
 
Drupal Lightning FAPI Jumpstart
Drupal Lightning FAPI JumpstartDrupal Lightning FAPI Jumpstart
Drupal Lightning FAPI Jumpstart
guestfd47e4c7
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniter
ciconf
 
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
BMMA - Belgian Management and Marketing Association
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
mussawir20
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
guest48224
 

What's hot (17)

Keeping It Simple
Keeping It SimpleKeeping It Simple
Keeping It Simple
 
Cube-S : Storytelling and Panchatantra
Cube-S : Storytelling and PanchatantraCube-S : Storytelling and Panchatantra
Cube-S : Storytelling and Panchatantra
 
Everything you always wanted to know about forms* *but were afraid to ask
Everything you always wanted to know about forms* *but were afraid to askEverything you always wanted to know about forms* *but were afraid to ask
Everything you always wanted to know about forms* *but were afraid to ask
 
Symfony day 2016
Symfony day 2016Symfony day 2016
Symfony day 2016
 
Devs for Leokz e 7Masters - WTF Oriented Programming
Devs for Leokz e 7Masters - WTF Oriented ProgrammingDevs for Leokz e 7Masters - WTF Oriented Programming
Devs for Leokz e 7Masters - WTF Oriented Programming
 
Leveraging Symfony2 Forms
Leveraging Symfony2 FormsLeveraging Symfony2 Forms
Leveraging Symfony2 Forms
 
Codigo taller-plugins
Codigo taller-pluginsCodigo taller-plugins
Codigo taller-plugins
 
Quality code by design
Quality code by designQuality code by design
Quality code by design
 
How to count money using PHP and not lose money
How to count money using PHP and not lose moneyHow to count money using PHP and not lose money
How to count money using PHP and not lose money
 
SQL Injection Part 2
SQL Injection Part 2SQL Injection Part 2
SQL Injection Part 2
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Pervasive 2011 Talk on Situated Glyphs
Pervasive 2011 Talk on Situated GlyphsPervasive 2011 Talk on Situated Glyphs
Pervasive 2011 Talk on Situated Glyphs
 
Drupal Lightning FAPI Jumpstart
Drupal Lightning FAPI JumpstartDrupal Lightning FAPI Jumpstart
Drupal Lightning FAPI Jumpstart
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniter
 
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
Workshop digital 6 - Social Media - Addressing new consumers expectations (Am...
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
 

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

State Machines to State of the Art
State Machines to State of the ArtState Machines to State of the Art
State Machines to State of the Art
Rowan Merewood
 
Drupal Development (Part 2)
Drupal Development (Part 2)Drupal Development (Part 2)
Drupal Development (Part 2)
Jeff Eaton
 
Using R for Building a Simple and Effective Dashboard
Using R for Building a Simple and Effective DashboardUsing R for Building a Simple and Effective Dashboard
Using R for Building a Simple and Effective Dashboard
Andrea Gigli
 
R57.Php
R57.PhpR57.Php
R57.Php
guest63876e
 
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
James Titcumb
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
James Titcumb
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
Mariusz Kozłowski
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
Fernando Daciuk
 
Symfony2
Symfony2Symfony2
Symfony2
mdpatrick
 
Get into the FLOW with Extbase
Get into the FLOW with ExtbaseGet into the FLOW with Extbase
Get into the FLOW with Extbase
Jochen Rau
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Balázs Tatár
 
Movie ticket booking
Movie ticket bookingMovie ticket booking
Movie ticket booking
Rutul Dave
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
elliando dias
 
Paypal + symfony
Paypal + symfonyPaypal + symfony
Paypal + symfony
Massimiliano Arione
 
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
James Titcumb
 
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, GermanyLet's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Balázs Tatár
 
Building Your First Widget
Building Your First WidgetBuilding Your First Widget
Building Your First Widget
Chris Wilcoxson
 
OWASP Top 10 - DrupalCon Amsterdam 2019
OWASP Top 10 - DrupalCon Amsterdam 2019OWASP Top 10 - DrupalCon Amsterdam 2019
OWASP Top 10 - DrupalCon Amsterdam 2019
Ayesh Karunaratne
 
Itsecteam shell
Itsecteam shellItsecteam shell
Itsecteam shell
ady36
 
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)
James Titcumb
 

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

State Machines to State of the Art
State Machines to State of the ArtState Machines to State of the Art
State Machines to State of the Art
 
Drupal Development (Part 2)
Drupal Development (Part 2)Drupal Development (Part 2)
Drupal Development (Part 2)
 
Using R for Building a Simple and Effective Dashboard
Using R for Building a Simple and Effective DashboardUsing R for Building a Simple and Effective Dashboard
Using R for Building a Simple and Effective Dashboard
 
R57.Php
R57.PhpR57.Php
R57.Php
 
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
 
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)Crafting Quality PHP Applications (Bucharest Tech Week 2017)
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
Symfony2
Symfony2Symfony2
Symfony2
 
Get into the FLOW with Extbase
Get into the FLOW with ExtbaseGet into the FLOW with Extbase
Get into the FLOW with Extbase
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
 
Movie ticket booking
Movie ticket bookingMovie ticket booking
Movie ticket booking
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Paypal + symfony
Paypal + symfonyPaypal + symfony
Paypal + symfony
 
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
 
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, GermanyLet's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
Let's write secure Drupal code! - 13.09.2018 @ Drupal Europe, Darmstadt, Germany
 
Building Your First Widget
Building Your First WidgetBuilding Your First Widget
Building Your First Widget
 
OWASP Top 10 - DrupalCon Amsterdam 2019
OWASP Top 10 - DrupalCon Amsterdam 2019OWASP Top 10 - DrupalCon Amsterdam 2019
OWASP Top 10 - DrupalCon Amsterdam 2019
 
Itsecteam shell
Itsecteam shellItsecteam shell
Itsecteam shell
 
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)
 

Recently uploaded

UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
Peter Muessig
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
Green Software Development
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
mz5nrf0n
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
Boni García
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
kalichargn70th171
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Łukasz Chruściel
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
Rakesh Kumar R
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
lorraineandreiamcidl
 

Recently uploaded (20)

UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
Fundamentals of Programming and Language Processors
Fundamentals of Programming and Language ProcessorsFundamentals of Programming and Language Processors
Fundamentals of Programming and Language Processors
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
 

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

  • 1. BIGLIETTI, PREGO!a ticket for the (command) bus
  • 2. @magobaol UNA SERIE SCRITTA, SCENEGGIATA E NARRATA DA
  • 7. Giulia è la direttrice generale di una compagnia di treni ad alta velocità
  • 8. Giulia commissiona a Roberto un software per la vendita di biglietti online.
  • 10. src !"" CustomerBundle !"" TicketBundle #   !"" Controller #   !"" DataFixtures #   !"" Entity #   !"" Form #   !"" Listener #   %"" Service | %"" Twig !"" TrainBundle %"" UserBundle TicketOffice Project
  • 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. 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. 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. 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;
  • 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. 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. 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. 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. 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.
  • 24.
  • 25. 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'); }
  • 26.
  • 28.
  • 29.
  • 30. 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'); }
  • 31.
  • 32. UN (ALTRO) PICCOLO MIGLIORAMENTO s01e06
  • 33.
  • 34. 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'); }
  • 35.
  • 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($customer->getEmail(), $ticket); $PNRSMSSender = $this->get('pnr_sms_sender'); $PNRSMSSender->send($customer->getMobile(), $PNR); return $this->render('thank_you_page.html.twig'); }
  • 38.
  • 39. 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'); }
  • 40. 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
  • 41. 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'); }
  • 42. 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'); }
  • 43. 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
  • 44.
  • 46.
  • 47. 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'); }
  • 48. 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'); }
  • 50. 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'); }
  • 52. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker ) { $this->seatNumberPicker = $seatNumberPicker; } }
  • 53. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; } }
  • 54. TicketCreatorService.php class TicketCreatorService { public function __construct( SeatNumberPicker $seatNumberPicker, PNRGenerator $PNRGenerator, EntityRepository $customerRepository ) { $this->seatNumberPicker = $seatNumberPicker; $this->PNRGenerator = $PNRGenerator; $this->customerRepository = $customerRepository; } }
  • 55. 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; } }
  • 56. 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; } }
  • 57. 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; } }
  • 58. 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; } }
  • 59. 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); }
  • 61. 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
  • 62. 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'); }
  • 63. 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'); }
  • 64. 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'); }
  • 65. 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); }
  • 66.
  • 67. 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'); }
  • 68. 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'); }
  • 69.
  • 72. 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); } }
  • 73. 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'); }
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 81.
  • 82. Disclaimer: @agilegigi non ha mai detto questa cosa, ma secondo me potrebbe farlo.
  • 83. 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'); }
  • 84. 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); }
  • 86. PRIMA O POI C’È SEMPRE UN LUNEDì s01e10
  • 87.
  • 89. 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'); }
  • 90. 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'); }
  • 91.
  • 93. TRA LA PRIMA E LA SECONDA STAGIONE...
  • 94.
  • 102. thanks to @leopro for the inspiring picture COMMAND BUS
  • 104. È un canale di comunicazione che consente di inviare e ricevere messaggi in un'applicazione MESSAGE BUS
  • 105. I messaggi possono essere comandi ed eventi MESSAGE BUS
  • 106. 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)
  • 107. 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 ]
  • 112. . !"" app #   !"" Resources #   #   %"" views #   #   %"" default #   %"" config !"" bin !"" src #   %"" AppBundle #   %"" Controller !"" tests #   %"" AppBundle #   %"" Controller !"" var #   !"" cache #   !"" logs #   %"" sessions %"" web Symfony 3 folders
  • 113. . !"" app #   !"" Resources #   #   %"" views #   #   %"" default #   %"" config !"" bin !"" src #   %"" AppBundle #   %"" Controller !"" tests #   %"" AppBundle #   %"" Controller !"" var #   !"" cache #   !"" logs #   %"" sessions %"" web Symfony 3 folders !"" src #   %"" AppBundle #   %"" Controller
  • 119. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity Domain folder
  • 120. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity %"" Ticket.php Domain folder
  • 121. 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; } }
  • 122.
  • 123. 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; } }
  • 124. Somewhere.php $rectangle = new Rectangle(); $rectangle->setWidth(100); $rectangle->setHeight(50);
  • 125. Somewhere.php $rectangle = new Rectangle(); $rectangle->setWidth(100); $rectangle->setHeight(50); $rectangle = new Rectangle(); NON È UN RETTANGOLO!
  • 126. 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; } }
  • 127. Somewhere.php $rectangle = new Rectangle(100, 50); QUESTO SÌ
  • 128. 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; } }
  • 129. app !"" DoctrineMigrations !"" Resources %"" config %"" config.yml config.yml
  • 130. 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
  • 131. 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
  • 132. 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'
  • 133. src !"" AppBundle #   !"" Controller #   !"" Entity #   %"" Resources #   %"" config #   %"" doctrine #   %"" Sale #   %"" Ticket.orm.yml %"" TicketOffice Ticket.orm.yml
  • 134. 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
  • 136. src !"" AppBundle %"" TicketOffice %"" Sale %"" Entity !"" Ticket.php %"" TicketRepository.php TicketRepository.php
  • 137. TicketRepository.php interface TicketRepository { public function save(Ticket $ticket); /** * @param $id * @return Ticket */ public function findById($id); }
  • 138. src !"" AppBundle #   !"" Controller #   !"" Entity #   #   %"" DoctrineTicketRepository.php #   %"" Resources %"" TicketOffice DoctrineTicketRepository.php
  • 139. 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); } }
  • 140. 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
  • 141. 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); } }
  • 142. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  • 145. src !"" AppBundle %"" TicketOffice %"" Sale !"" Entity %"" Service %"" SeatPicker.php SeatPicker.php
  • 146. class SeatPicker { public function pick($trainNumber, $departure) { return rand(1, 1000); } } SeatPicker.php
  • 147. src !"" AppBundle %"" TicketOffice %"" Sale !"" Entity %"" Service !"" PNRGenerator.php %"" SeatPicker.php PNRGenerator.php
  • 148. class PNRGenerator { public function generate( $trainNumber, $departure, $seatNumber ) { return uniqid(); } } PNRGenerator.php
  • 149. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  • 154. AppKernel.php public function registerBundles() { $bundles = [ //... new SimpleBusSymfonyBridgeSimpleBusCommandBusBundle(), new SimpleBusSymfonyBridgeSimpleBusEventBusBundle(), ]; //... }
  • 155. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command #   %"" SellTicket.php !"" Entity %"" Service SellTicket.php
  • 156. 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() {...} }
  • 157. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command #   !"" SellTicket.php #   %"" SellTicketHandler.php !"" Entity %"" Service SellTicketHandler.php
  • 158. 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; } }
  • 159. 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
  • 160. 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); }
  • 161. 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)
  • 162. 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() );
  • 163. 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 );
  • 164. 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 );
  • 165. 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);
  • 166. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  • 167. 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 }
  • 168. 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"
  • 169. 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 }
  • 171. src !"" AppBundle #   !"" Controller #   #   %"" TicketController.php #   !"" Entity #   %"" Resources %"" TicketOffice TicketController.php
  • 172. 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); }
  • 173. 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();
  • 174. 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')) );
  • 175. 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);
  • 176. 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);
  • 178. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity # !"" Customer.php #   !"" Ticket.php #   %"" TicketRepository.php %"" Service Customer.php
  • 179. 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() {...} }
  • 180. src !"" AppBundle #   !"" Controller #   !"" Entity #   %"" Resources #   %"" config #   %"" doctrine #   %"" Sale #   %"" Customer.orm.yml %"" TicketOffice Customer.orm.yml
  • 181. Customer.orm.yml TicketOfficeSaleEntityCustomer: type: entity table: customer id: id: type: integer generator: { strategy: AUTO } fields: email: type: string
  • 182. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity # !"" Customer.php #   !"" CustomerRepository.php #   !"" Ticket.php #   %"" TicketRepository.php %"" Service CustomerRepository.php
  • 183. CustomerRepository.php interface CustomerRepository { /** * @param $id * @return Customer */ public function findById($id); }
  • 184. src !"" AppBundle #   !"" Controller #   !"" Entity #   #   %"" DoctrineCustomerRepository.php #   %"" Resources %"" TicketOffice DoctrineCustomerRepository.php
  • 185. 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); } }
  • 187. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event #   %"" TicketSold.php %"" Service TicketSold.php
  • 188. 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() {...} }
  • 189. 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; } }
  • 190. 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;
  • 191. 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); }
  • 192. 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() );
  • 193. 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);
  • 195. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Service %"" Subscriber %"" SendEmailWhenTicketSold.php SendEmailWhenTicketSold.php
  • 196. 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(...) {...} }
  • 197. 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
  • 198. 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); }
  • 199. 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
  • 200. 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() );
  • 201. 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() );
  • 202. 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);
  • 203. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  • 204. 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 }
  • 205. 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 }
  • 206. src !"" AppBundle %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Service %"" Subscriber !"" SendEmailWhenTicketSold.php %"" SendPNRWhenTicketSold.php SendPNRWhenTicketSold.php
  • 207. 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(...) {...} }
  • 208. 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()) ); }
  • 209. 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
  • 210. 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() );
  • 211. 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() );
  • 212. 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()) );
  • 213. app !"" DoctrineMigrations !"" Resources %"" config %"" services.yml services.yml
  • 214. 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 }
  • 215. 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 }
  • 217. 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); }
  • 218. 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);
  • 219. 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);
  • 220. src !"" AppBundle !"" Basic #   %"" TicketOfficeException.php %"" TicketOffice TicketOfficeException.php
  • 221. 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() {...} }
  • 222. src !"" AppBundle !"" Basic %"" TicketOffice %"" Sale !"" Command !"" Entity !"" Event !"" Exception #   %"" CustomerNotAllowedException.php !"" Service %"" Subscriber CustomerNotAllowedException.php
  • 223. CustomerNotAllowedException.php class CustomerNotAllowedException extends TicketOfficeException { protected $code = 666; protected $message = 'Customer is not allowed to use this service.'; }
  • 224. 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); }
  • 225. 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(); }
  • 226. 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); } }
  • 227. 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); }
  • 228. 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); }
  • 231. COMMAND ‣Si usa per muovere il dominio ‣Va a buon fine per definizione ‣Non restituisce nessun risultato
  • 232. COMMAND HANDLER ‣ Gestisce il comando ‣ Effettua validazioni di dominio ‣ Muove concretamente il dominio ‣ Solleva eccezioni in caso di problemi
  • 233. EVENT ‣ Viene sollevato a fronte di qualcosa accaduto nel dominio (es: UserRegistered) ‣ Contiene informazioni arbitrarie
  • 234. 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
  • 236. Giulia ha continuato a chiedere nuove funzionalità perché un software non è mai finito
  • 237. Roberto ha potuto implementarle senza scrivere controller o servizi di 2000 righe
  • 238. I comandi, gli eventi e i subscriber sono piccoli e perciò facilmente testabili
  • 239. Quindi Roberto ha potuto consegnare nuove funzionalità più velocemente e con meno errori
  • 240. (anche) grazie al software di Roberto, l'azienda di Giulia prospera e vende biglietti
  • 245. 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