Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplify your application with a command bus

Simplify your application with a command bus

Introduction to the command bus pattern, and how to set it up on your Symfony application.
Talk given at SymfonyCon Cluj 2017


Romaric Drigon

November 17, 2017


  1. Simplify your application with a command bus SymfonyCon Cluj 2017

  2. Romaric Drigon Software engineer @ netinfluence, Switzerland

  3. What we want to avoid

  4. How our code should feel like

  5. What is a command bus? A command... "...an object is

    used to encapsulate all information needed to perform an action..." ...and a bus "...a communication system that transfers data between components..."
  6. To wrap up...

  7. A use case A Finder-like application in javascript On each

    file, you can operate commands: create a folder, rename a file, remove... Requests are sent from the JS application to a Symfony backend
  8. Setting up a command bus We will use Tactician (https://tactician.thephpleague.com/)

    Developed by Ross Tuck A Symfony bundle is available Notable alternatives include SimpleBus, by Matthias Noback
  9. A Command <?php namespace AppBundle\Model\Command; use AppBundle\Entity\Item; class RemoveCommand {

    private $item; public function __construct(Item $item) { $this->item = $item; } public function getItem(): Item { return $this->item; } }
  10. The corresponding handler <?php namespace AppBundle\Handler; use AppBundle\Entity\Item; use AppBundle\Model\Command\RemoveCommand;

    class RemoveHandler { private $entityManager; // To inject public function handle(RemoveCommand $removeCommand): string { $item = $removeCommand->getItem(); $this->entityManager->remove($item); $this->entityManager->flush(); // We can find a better way return $item->getUuid(); // It can return a result! } }
  11. Declaring the handler Declared as a Symfony service: Note: handlers

    now support autowiring, check out Tactician bundle documentation app.remove_handler: class: AppBundle\Handler\RemoveHandler tags: - { name: tactician.handler, command: AppBundle\Model\Command\RemoveComman arguments: - '@doctrine.orm.default_entity_manager'
  12. Sending the command to the bus From the Symfony API

    controller: Great, Romaric... but what is the interest? /** * @ParamConverter("item") */ public function apiAction(Item $item) { $command = new RemoveCommand($item); $uuid = $this->get('tactician.commandbus')->handle($command); return new JsonResponse(['uuid' => $uuid]); }
  13. It scales well!

  14. The application is getting complex... After one month, we had

    25 commands and handlers, totaling 3500 lines of code.
  15. Adding middlewares Huge interest: middlewares help to simplify common tasks.

  16. Middleware examples Doctrine (https://tactician.thephpleague.com/plugins/doctrine/) : wraps handlers in a DB

    transaction Logger (https://tactician.thephpleague.com/plugins/logger/) : can log everything to Monolog (audit!) Validation (shipped with Symfony Tactician bundle) Security, authorization ...
  17. Going further Testing: handlers and middlewares are easier to unit

    test Commands could be sent to a message queue (RabbitMQ...) instead of staying in memory, or scheduled to be executed later (delayed task...).
  18. romaric@netinfluence.ch http://blog.netinfluence.ch