Command bus in Symfony application

Creating a command bus in a Symfony project is really easy. Most of the work is done by the composer recipes and the default configuration is usually almost enough to run a command bus.

Let’s get started

I created a simple project template to save you time. To install it, just run the below commands.

git clone -b project_template
cd ./messenger-symfony-example
composer install

Afterward, paste your MySQL credentials in .env file. Then execute commands that create a database and run migrations.

php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate

Finally, run a development server.

php -S localhost:8000 -t public

Bus configuration

First, we need to install the Messenger component. If you want to learn more about Messenger check this post. During installation, Symfony Flex is going to create configuration files automatically.

composer require messenger

When the component is installed, we need to configure a command bus and set it as a default bus. Our command bus is going to work synchronously so we don’t have to configure any transports.

// config/packages/messenger.yaml

    default_bus: command.bus
                    - doctrine_transaction

We also need to ensure that our command handlers are available only for the command bus.

// config/services.yaml

    // ...
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

        autoconfigure: false
        resource: '../src/Command/Handler'
        tags: [{ name: messenger.message_handler, bus: command.bus }]

Command and command handler

Now let’s create our first command and command handler.

// src/Command/AddNote.php

namespace App\Command;

use Ramsey\Uuid\Uuid;

class AddNote
    private $id;
    private $title;
    private $description;

    public function __construct(Uuid $noteId, string $noteTitle, string $noteDescription)
        $this->id = $noteId;
        $this->title = $noteTitle;
        $this->description = $noteDescription;

    public function getId(): Uuid
        return $this->id;

    public function getTitle(): string
        return $this->title;

    public function getDescription(): string
        return $this->description;
// src/Command/Handler/AddNoteHandler.php

namespace App\Command\Handler;

use App\Command\AddNote;
use App\Entity\Note;
use App\Repository\NoteRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class AddNoteHandler implements MessageHandlerInterface
    private $notes;
    private $em;

    public function __construct(NoteRepository $noteRepository, EntityManagerInterface $em)
        $this->notes = $noteRepository;
        $this->em = $em;

    public function __invoke(AddNote $command)
        $noteId = $command->getId();
        $noteTitle = $command->getTitle();
        $noteDescription = $command->getDescription();

        if($this->notes->noteWithTitleExists($noteTitle)) {
            throw new \LogicException("Note title has to be unique");

        $note = new Note($noteId);


Then, we have to add a method, which we used in the handler, to our repository class.

// src/Repository/NoteRepository.php


namespace App\Repository;

use App\Entity\Note;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class NoteRepository extends ServiceEntityRepository
    public function __construct(ManagerRegistry $registry)
        parent::__construct($registry, Note::class);

    public function noteWithTitleExists(string $title): bool
        return !is_null($this->getNoteByTitle($title));

    public function getNoteByTitle(string $title): ?Note
        return $this->findOneBy(['title' => $title]);

In the end, we just need to implement command dispatching in NoteController.

// src/Controller/NoteController.php

// ...
class NoteController extends AbstractController
     * @Route("/note/add", methods={"POST"})
    public function add(Request $request, MessageBusInterface $commandBus)
        $noteId = Uuid::uuid4();
        $noteTitle = $request->get("noteTitle");
        $noteDescription = $request->get("noteDescription");

        try {
                new AddNote($noteId, $noteTitle, $noteDescription)
        } catch (\Exception $e) {
            //TODO Handle exception

        return $this->redirectToRoute('index');

Deleting notes

In the same way, we can implement deleting notes. All required changes are contained in this commit.


All code up to this point is available here. You can also check the next posts about creating buses in Symfony project:

You May Also Like

2 Comments to “Command bus in Symfony application”

  1. Gratz, great post !. Just a little comment … if you use the “middleware doctrine_transaction” then you don’t need to do “$this->em->flush()” on handler!


    1. Thank you for your comment!
      You are totally right that DoctrineTransactionMiddleware wraps the entire handler in a transaction and there’s no need to execute flush() manually. However, I decided to leave it as is because using the middleware is not necessary and it might be confusing for devs who don’t use it. In addition, executing flush() method twice is harmless.
      Nevertheless, I will add an alert box with that information soon. Thanks for the suggestion!

Leave a Reply

Your email address will not be published. Required fields are marked *