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

0d02cc7698020597fdff97397baf799b?s=128

Romaric Drigon

November 17, 2017
Tweet

Transcript

  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