Reservations pour in from the four corners of the kingdom, and Gorm forgets half of the confirmations. So the tavern gets itself an aviary: for every reservation, a raven flies off to deliver the confirmation.
The controller could write the raven itself. But confirming a reservation is not its job: its job is to answer the scroll. So it will announce what just happened, and someone else will take care of it.
Your mission
ConfirmationDemandee(confirmation requested): the message. It only carries the reservation's identifier.ConfirmationDemandeeHandler: the handler, which finds the reservation and records the raven (Corbeau):destinataire(recipient): the reservation'snom(name);message:Table booked for 4 adventurers on September 18, 2026. See you soon!(singular for a single adventurer);- if the reservation has vanished, it does nothing, without throwing an error.
- In
POST /api/reservations, dispatch the message once the reservation is saved.
The aviary is displayed at /aviary. Book from the “Requests” tab, then look at the page.
Message, handler, bus
$bus->dispatch(new ConfirmationDemandee($reservation->getId()));
The bus passes the message on to its handler: the class marked #[AsMessageHandler] whose __invoke() method accepts this type of message. No direct link between the two: the controller does not know who handles the message, and the handler does not know where the request came from.
For now, everything happens during the request. In the next exercise, the raven will fly in the background.
Why the identifier, and not the reservation?
A message is made to travel: soon it will be serialized, queued, and read again later. A Doctrine entity travels badly. With the identifier, the handler reads the reservation back at the moment it works, so it is up to date.