taming command bus
TRANSCRIPT
![Page 1: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/1.jpg)
Taming
Command Bus
Krzysztof Menżyk @kmenzyk
![Page 2: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/2.jpg)
About me
Technical Leader at Test infectedBelieves that software is a craftLoves domain modelling
Homebrewer & squash player
![Page 3: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/3.jpg)
This talk is not
![Page 4: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/4.jpg)
This talk is not
DDD*
![Page 5: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/5.jpg)
This talk is not
CQRS*
![Page 6: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/6.jpg)
Back to the
PAST
![Page 7: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/7.jpg)
BEGANIt all
with ...
![Page 8: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/8.jpg)
class ProductController extends Controller{ //...
public function updateAction($id) { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); }
$em = $this->getDoctrine()->getManager(); $product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) { throw $this->createNotFoundException('Unable to find Product entity.'); }
$request = $this->getRequest(); $editForm = $this->createForm(new ProductType(), $product); $editForm->bind($request);
if ($editForm->isValid()) { $product->setLastUpdated(new \DateTime); $em->flush();
$message = \Swift_Message::newInstance() ->setSubject('Product updated') ->setFrom($this->container->getParameter('acme.product_email.from')) ->setTo($this->container->getParameter('acme.product_email.to')) ->setBody($this->renderView( 'AcmeDemoBundle:Product:email.txt.twig', array('product' => $product)) ) ; $this->get('mailer')->send($message);
return $this->redirect( $this->generateUrl('product', array('id' => $id)) ); }
return new Response( $this->renderView( 'AcmeDemoBundle:Product:edit.html.twig', array( 'product' => $product, 'edit_form' => $editForm->createView(), ) ) ); }}
![Page 9: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/9.jpg)
Controller Driven Design
„approach”
![Page 10: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/10.jpg)
class ProductController extends Controller{ //...
public function updateAction($id) { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); }
$em = $this->getDoctrine()->getManager(); $product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) { throw $this->createNotFoundException('Unable to find Product entity.'); }
$request = $this->getRequest(); $editForm = $this->createForm(new ProductType(), $product); $editForm->bind($request);
if ($editForm->isValid()) { $product->setLastUpdated(new \DateTime); $em->flush();
$message = \Swift_Message::newInstance() ->setSubject('Product updated') ->setFrom($this->container->getParameter('acme.product_email.from')) ->setTo($this->container->getParameter('acme.product_email.to')) ->setBody($this->renderView( 'AcmeDemoBundle:Product:email.txt.twig', array('product' => $product)) ) ; $this->get('mailer')->send($message);
return $this->redirect( $this->generateUrl('product', array('id' => $id)) ); }
return new Response( $this->renderView( 'AcmeDemoBundle:Product:edit.html.twig', array( 'product' => $product, 'edit_form' => $editForm->createView(), ) ) ); }}
![Page 11: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/11.jpg)
class ProductController extends Controller{ //...
public function updateAction($id) { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); }
$em = $this->getDoctrine()->getManager(); $product = $em->getRepository('AcmeDemoBundle:Product')->find($id);
if (!$product) { throw $this->createNotFoundException('Unable to find Product entity.'); }
$request = $this->getRequest(); $editForm = $this->createForm(new ProductType(), $product); $editForm->bind($request);
if ($editForm->isValid()) { $product->setLastUpdated(new \DateTime); $em->flush();
$message = \Swift_Message::newInstance() ->setSubject('Product updated') ->setFrom($this->container->getParameter('acme.product_email.from')) ->setTo($this->container->getParameter('acme.product_email.to')) ->setBody($this->renderView( 'AcmeDemoBundle:Product:email.txt.twig', array('product' => $product)) ) ; $this->get('mailer')->send($message);
return $this->redirect( $this->generateUrl('product', array('id' => $id)) ); }
return new Response( $this->renderView( 'AcmeDemoBundle:Product:edit.html.twig', array( 'product' => $product, 'edit_form' => $editForm->createView(), ) ) ); }}
![Page 12: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/12.jpg)
LAYERS
![Page 13: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/13.jpg)
Presentation
![Page 14: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/14.jpg)
Presentation
Domain
![Page 15: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/15.jpg)
Presentation
Service
Domain
![Page 16: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/16.jpg)
Presentation
Service
Domain
Infrastructure
![Page 17: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/17.jpg)
Presentation
Service
Domain
Infrastructure
![Page 18: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/18.jpg)
Defines an application's boundary with a layer of services.
Service Layer
![Page 19: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/19.jpg)
Establishes set of available operations.
Service Layer
![Page 20: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/20.jpg)
Coordinates the application's response in each operation.
Service Layer
![Page 21: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/21.jpg)
USE Cases
![Page 22: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/22.jpg)
USER STORIES
![Page 23: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/23.jpg)
Application Layer
![Page 24: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/24.jpg)
class Library{ public function borrowBook($bookId, $readerId) { // ... }}
![Page 25: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/25.jpg)
class LibraryController{ public function borrowAction(ServerRequestInterface $request) { // Forms, etc.
$this->library->borrowBook($bookId, $readerId); // Response }}
![Page 26: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/26.jpg)
class Library{ public function borrowBook($bookId, $readerId) { if (null === $bookId || null === $readerId) { throw new \InvalidArgumentException(); }
$this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction(); try { $book = $this->books->get($bookId); $book->borrowBy($readerId); $this->books->add($book); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); $this->logging->debug('Ooops! Bad luck.'); throw $e; }
$this->logging->debug('Success!'); }}
![Page 27: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/27.jpg)
class Library{ public function borrowBook($bookId, $readerId) { if (null === $bookId || null === $readerId) { throw new \InvalidArgumentException(); }
$this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction(); try { $book = $this->books->get($bookId); $book->borrowBy($readerId); $this->books->add($book); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); $this->logging->debug('Ooops! Bad luck.'); throw $e; }
$this->logging->debug('Success!'); }}
![Page 28: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/28.jpg)
class Library{ public function borrowBook($bookId, $readerId) { if (null === $bookId || null === $readerId) { throw new \InvalidArgumentException(); }
$this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction(); try { $book = $this->books->get($bookId); $book->borrowBy($readerId); $this->books->add($book); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); $this->logging->debug('Ooops! Bad luck.'); throw $e; }
$this->logging->debug('Success!'); }}
![Page 29: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/29.jpg)
class Library{ public function borrowBook($bookId, $readerId) { if (null === $bookId || null === $readerId) { throw new \InvalidArgumentException(); }
$this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction(); try { $book = $this->books->get($bookId); $book->borrowBy($readerId); $this->books->add($book); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); $this->logging->debug('Ooops! Bad luck.'); throw $e; }
$this->logging->debug('Success!'); }}
![Page 30: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/30.jpg)
class Library{ public function borrowBook($bookId, $readerId) { if (null === $bookId || null === $readerId) { throw new \InvalidArgumentException(); }
$this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction(); try { $book = $this->books->get($bookId); $book->borrowBy($readerId); $this->books->add($book); $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); $this->logging->debug('Ooops! Bad luck.'); throw $e; }
$this->logging->debug('Success!'); }}
![Page 31: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/31.jpg)
borrowBook($bookId, $readerId)
![Page 32: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/32.jpg)
new BorrowBook($bookId, $readerId)
![Page 33: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/33.jpg)
COMMAND
![Page 34: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/34.jpg)
not a class/objectMESSAGE
![Page 35: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/35.jpg)
Messaging Flavors
by @mathiasverraes
![Page 36: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/36.jpg)
Messaging Flavors
by @mathiasverraes
Imperative
![Page 37: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/37.jpg)
Messaging Flavors
by @mathiasverraes
Imperative
Command
![Page 38: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/38.jpg)
Messaging Flavors
by @mathiasverraes
Imperative Interrogatory
Command
![Page 39: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/39.jpg)
Messaging Flavors
by @mathiasverraes
Imperative Interrogatory
Command QUERY
![Page 40: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/40.jpg)
Messaging Flavors
by @mathiasverraes
Imperative Interrogatory Informational
Command QUERY
![Page 41: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/41.jpg)
Messaging Flavors
by @mathiasverraes
Imperative Interrogatory Informational
Command QUERY EVENT
![Page 42: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/42.jpg)
Messaging Flavors
by @mathiasverraes
Imperative Interrogatory Informational
Command QUERY EVENT
![Page 43: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/43.jpg)
Captures
THE INTENTof the user
![Page 44: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/44.jpg)
Supports
UbiquitousLANGUAGE
![Page 45: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/45.jpg)
Borrow BookGive Book BackRegister User
Start SubscriptionHire Employee
![Page 46: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/46.jpg)
Contains
THE INPUTto carry out the task
![Page 47: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/47.jpg)
final class BorrowBook{ public $bookId; public $readerId;
public function __construct($bookId, $readerId) { $this->bookId = $bookId; $this->readerId = $readerId; }}
![Page 48: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/48.jpg)
Should be
IMMUTABLE
![Page 49: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/49.jpg)
final class BorrowBook{ private $bookId; private $readerId;
public function __construct($bookId, $readerId) { $this->bookId = $bookId; $this->readerId = $readerId; }
public function getBookId() { return $this->bookId; }
public function getReaderId() { return $this->readerId; }}
![Page 50: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/50.jpg)
Must be
VALID
![Page 51: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/51.jpg)
final class BorrowBook{ private $bookId; private $readerId;
public function __construct($bookId, $readerId) { Assertion::notNull($bookId); Assertion::notNull($readerId); $this->bookId = $bookId; $this->readerId = $readerId; }
public function getBookId() { return $this->bookId; }
public function getReaderId() { return $this->readerId; }}
![Page 52: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/52.jpg)
INTERPRETHow to
a command?
![Page 53: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/53.jpg)
COMMANDHandler
![Page 54: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/54.jpg)
per commandONE HANDLER
![Page 55: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/55.jpg)
Returns
NO VALUE
![Page 56: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/56.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction();
try { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);
$this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack();
$this->logging->debug('Ooops! Bad luck.');
throw $e; }
$this->logging->debug('Success!'); }}
![Page 57: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/57.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction();
try { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);
$this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack();
$this->logging->debug('Ooops! Bad luck.');
throw $e; }
$this->logging->debug('Success!'); }}
![Page 58: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/58.jpg)
One more thing ...
![Page 59: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/59.jpg)
The BUS
![Page 60: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/60.jpg)
COMMANDBUS
![Page 61: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/61.jpg)
Hands over
THE COMMANDto
THE HANDLEr
![Page 62: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/62.jpg)
Handler C
CommandBus
Handler BHandler A
![Page 63: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/63.jpg)
Handler C
Command C
CommandBus
Handler BHandler A
![Page 64: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/64.jpg)
Handler C
CommandBus
Handler BHandler A
Command C
![Page 65: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/65.jpg)
Handler C
CommandBus
Handler BHandler A Command C
![Page 66: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/66.jpg)
Handler C
CommandBus
Handler BHandler A
![Page 67: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/67.jpg)
Single
ENTRY POINTto the application
![Page 68: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/68.jpg)
interface CommandBus{ public function handle($command);}
![Page 69: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/69.jpg)
class LibraryController{ public function borrowAction(ServerRequestInterface $request) { // Forms, etc.
$this->commandBus->handle(new BorrowBook($bookId, $readerId));
// Response }}
![Page 70: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/70.jpg)
class SomeOtherController{ public function someOtherAction(ServerRequestInterface $request) { // Forms, etc.
$command = $form->getData(); $this->commandBus->handle($command);
// Response }}
![Page 71: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/71.jpg)
class SomeOtherController{ public function someOtherAction(ServerRequestInterface $request) { // Forms, etc.
$command = $form->getData(); $this->commandBus->handle($command);
// Response }}
![Page 72: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/72.jpg)
class BorrowBookCli{ protected function execute(InputInterface $input, OutputInterface $output) { // get $command from the input somehow $this->commandBus->handle($command);
// ... }}
![Page 73: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/73.jpg)
IMPLEMENTLet's
the stupid thing
![Page 74: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/74.jpg)
class SimpleCommandBus implements CommandBus{ private $handlers;
public function __construct(array $handlers) { $this->handlers = $handlers; }
public function handle($command) { $commandName = get_class($command);
if (!isset($this->handlers[$commandName])) { throw new \InvalidArgumentException('No handler'); } $this->handlers[$commandName]->handle($command); }}
![Page 75: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/75.jpg)
new SimpleCommandBus([ BorrowBook::class => $borrowBookHandler, GiveBookBack::class => $giveBookBackHandler,]);
![Page 76: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/76.jpg)
That's it?
![Page 77: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/77.jpg)
Do not
TRY ITat home
![Page 78: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/78.jpg)
class SimpleCommandBus implements CommandBus{ private $handlers;
public function __construct(array $handlers) { $this->handlers = $handlers; }
public function handle($command) { $commandName = get_class($command);
if (!isset($this->handlers[$commandName])) { throw new \InvalidArgumentException('No handler'); } $this->handlers[$commandName]->handle($command); }}
![Page 79: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/79.jpg)
Cross cutting concerns
![Page 80: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/80.jpg)
interface CommandBus{ public function handle($command);}
![Page 81: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/81.jpg)
DECORATOR
![Page 82: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/82.jpg)
class CommandBusWithAddedBehavior implements CommandBus{ public function __construct(CommandBus $originalCommandBus) { $this->originalCommandBus = $originalCommandBus; }
public function handle($command) { // do anything you want
$this->originalCommandBus->handle($command);
// do even more }}
![Page 83: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/83.jpg)
new CommandBusWithAddedBehavior( new SimpleCommandBus([ BorrowBook::class => $borrowBookHandler, GiveBookBack::class => $giveBookBackHandler, ]));
![Page 84: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/84.jpg)
Command Bus
Decorator
Decorator
Command
![Page 85: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/85.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction();
try { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);
$this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack();
$this->logging->debug('Ooops! Bad luck.');
throw $e; }
$this->logging->debug('Success!'); }}
![Page 86: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/86.jpg)
class TransactionalCommandBus implements CommandBus{ public function __construct($innerCommandBus, $connection) { $this->innerCommandBus = $innerCommandBus; $this->connection = $connection; }
public function handle($command) { $this->connection->beginTransaction(); try { $this->innerCommandBus->handle($command); $this->connection->commit(); } catch (\Exception $exception) { $this->connection->rollBack();
throw $e; } }}
![Page 87: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/87.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction();
try { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);
$this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack();
$this->logging->debug('Ooops! Bad luck.');
throw $e; }
$this->logging->debug('Success!'); }}
![Page 88: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/88.jpg)
class LoggingCommandBus implements CommandBus{ public function __construct($innerCommandBus, $logger) { $this->innerCommandBus = $innerCommandBus; $this->logger = $logger; }
public function handle($command) { $commandClass = get_class($command);
$this->logger->debug('Started handling ' . $commandClass);
try { $this->innerCommandBus->handle($command); } catch (\Exception $exception) { $this->logger->debug('Error while handling ' . $commandClass);
throw $e; }
$this->logger->debug('Finished handling ' . $commandClass); }}
![Page 89: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/89.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $this->logging->debug('A reader is borrowing a book');
$this->connection->beginTransaction();
try { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);
$this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack();
$this->logging->debug('Ooops! Bad luck.');
throw $e; }
$this->logging->debug('Success!'); }}
![Page 90: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/90.jpg)
final class BorrowBookHandler{ public function handle(BorrowBook $command) { $book = $this->books->get($command->getBookId()); $book->borrowBy($command->getReaderId());
$this->books->add($book);'Success!'); }}
![Page 91: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/91.jpg)
Easy to
TEST
![Page 92: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/92.jpg)
But...
![Page 93: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/93.jpg)
new LoggingCommandBus( new TransactionalCommandBus( new SimpleCommandBus([ BorrowBook::class => $borrowBookHandler, GiveBookBack::class => $giveBookBackHandler, ]), $connection ), $logger);
![Page 94: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/94.jpg)
Can we do better?
![Page 95: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/95.jpg)
Yes
We can!
![Page 96: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/96.jpg)
CHAIN OF RESPONSIBILITY
![Page 97: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/97.jpg)
interface CommandBusMiddleware{ public function handle($command, callable $next);}
![Page 98: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/98.jpg)
Command Handler
Middleware
Command
Middleware
Middleware
![Page 99: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/99.jpg)
class AddedBehaviorMiddleware implements CommandBusMiddleware{ public function handle($command, callable $next) { // do anything you want
$next($command);
// do even more }}
![Page 100: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/100.jpg)
new CommandBusSupportingMiddleware([ new AddedBehaviorMiddleware(), new CommandHandlerMiddleware([ BorrowBook::class => $borrowBookHandler, GiveBookBack::class => $giveBookBackHandler, ])]);
![Page 101: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/101.jpg)
new CommandBusSupportingMiddleware([ new LoggingMiddleware($logger), new TransactionalMiddleware($connection), new CommandHandlerMiddleware([ BorrowBook::class => $borrowBookHandler, GiveBookBack::class => $giveBookBackHandler, ])]);
![Page 102: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/102.jpg)
TransactionsLoggingSecurity
Performance MetricsAudit log
You name it
![Page 103: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/103.jpg)
Command Bus libraries
![Page 104: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/104.jpg)
by @matthiasnobackSimple Bus
![Page 105: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/105.jpg)
by @rosstuckTACTICIAN
![Page 106: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/106.jpg)
by @qandidate-labsBROADWAY
![Page 107: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/107.jpg)
Use them and contribute
![Page 108: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/108.jpg)
Like we
DID
![Page 109: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/109.jpg)
Krzysztof Menżyk @kmenzyk
Thanks!
Taming Command Bus
![Page 110: Taming Command Bus](https://reader031.vdocument.in/reader031/viewer/2022021814/58ef01c81a28ab466d8b4621/html5/thumbnails/110.jpg)
PHOTO CREDITShttps://flic.kr/p/F2qhc7https://flic.kr/p/zMzVzWhttps://flic.kr/p/F4j2fHhttps://flic.kr/p/5a5d3bhttps://flic.kr/p/vXAK3D