Dispatching Events

Previous pages provide the background on how to handle event messages in your application. The dispatching process is the starting point for event message. You will mostly send events after successfully handling Command Message, examples will base on that assumption.

You can inject EventBus and send events wherever necessary. Ecotone tries not to impose any specific solutions.

Event Bus

Event Bus is special type of Messaging Gateway.

namespace Ecotone\Modelling;
interface EventBus
{
1 public function send(object $event);
2 public function sendWithMetadata(object $event, array $metadata);
3 public function convertAndSend(string $name, string $sourceMediaType, $data);
4 public function convertAndSendWithMetadata(string $name, string $sourceMediaType, $data, array $metadata);
}

Send method

Event is routed to the Handler by class type.

Command Handler
Command Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\EventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, EventBus $eventBus) : void
{
// handle closing ticket
$eventBus->send(new TicketWasClosed($command->getTicketId()));
}
}
Event Handler
Event Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\EventHandler;
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler()
*/
public function closeTicket(TicketWasClosed $event) : void
{
// notify about closing the ticket
}
}

SendWithMetadata

Does allow for passing extra meta information, that can be used on targeted Event Handler.

Command Handler
Command Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\EventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, EventBus $eventBus) : void
{
// handle closing ticket
$eventBus->sendWithMetadata(new TicketWasClosed($command->getTicketId()), ["executorUsername" => "someUsername"]);
}
}
Event Handler
Event Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\EventHandler;
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler()
*/
public function closeTicket(TicketWasClosed $event, array $metadata) : void
{
// you can use $metadata["executorUsername"]
// notify about closing the ticket
}
}

ConvertAndSend

Is used with Command Handlers,routed by name and converted using Converter if needed. Sending events by name instead of class type, may be found useful in integration with external application, when events are in different Media Type than PHP class.

Command Handler
Command Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\EventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, EventBus $eventBus) : void
{
// handle closing ticket
$eventBus->convertAndSend(
"ticket.wasClosed",
"application/json",
'{"ticketId": 123}'
);
}
}
Event Handler
Event Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\EventHandler;
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler(listenTo="ticket.wasClosed")
*/
public function closeTicket(TicketWasClosed $event) : void
{
// notify about closing the ticket
}
}

JSON will be automatically converted to specific class type hinted in method declaration of Event Handler. You could also use in here simple array if you have JSON to array Converter or a string, if you like to receive JSON string.

ConvertAndSendWithMetadata

Same as convertAndSend with possibility to pass Metadata.

Command Handler
Command Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\EventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, EventBus $eventBus) : void
{
// handle closing ticket
$eventBus->convertAndSendWithMetadata(
"ticket.wasClosed",
"application/json",
'{"ticketId": 123}',
["executorUsername" => "someUsername"]
);
}
}
Event Handler
Event Handler
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\EventHandler;
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler(listenTo="ticket.wasClosed")
*/
public function closeTicket(TicketWasClosed $event, array $metadata) : void
{
// you can use $metadata["executorUsername"]
// notify about closing the ticket
}
}

Lazy Event Bus

Lazy Event Bus is special type of Messaging Gateway.

namespace Ecotone\Modelling;
interface LazyEventBus
{
1 public function send(object $event);
2 public function sendWithMetadata(object $event, array $metadata);
}

Send method

The difference between EventBus and LazyEventBus is the moment of EventHandler invocation. EventBus sends Event right away to all awaiting EventHandlers. LazyEventBus registers the Event and waiting for Command Handler to finish. When the Command Handling is done, then it publish previously registered events. Below we can comparison in execution order:

Lazy Event Bus
Event Bus
Lazy Event Bus
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\LazyEventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, LazyEventBus $eventBus) : void
{
echo "starting command handler";
$eventBus->send(new TicketWasClosed($command->getTicketId()));
echo "finished command handler";
}
}
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler()
*/
public function closeTicket(TicketWasClosed $event) : void
{
echo "notification for event handler"
}
}
-----
1. "starting command handler"
2. "finished command handler"
3. "notification for event handler"
Event Bus
use Ecotone\Messaging\Annotation\MessageEndpoint;
use Ecotone\Modelling\Annotation\CommandHandler;
use Ecotone\Modelling\LazyEventBus;
/**
* @MessageEndpoint()
*/
class CloseTicketCommandHandler
{
/**
* @CommandHandler()
*/
public function closeTicket(CloseTicketCommand $command, LazyEventBus $eventBus) : void
{
echo "starting command handler";
$eventBus->send(new TicketWasClosed($command->getTicketId()));
echo "finished command handler";
}
}
/**
* @MessageEndpoint()
*/
class NotificationService
{
/**
* @EventHandler()
*/
public function closeTicket(TicketWasClosed $event) : void
{
echo "notification for event handler"
}
}
-----
1. "starting command handler"
2. "notification for event handler"
3. "finished command handler"

SendWithMetadata

Does work the same as Send with possibility to send Metadata. Usage is no different than with standard Event Bus.

Publishing from Aggregate

The easiest way to publish events from aggregate would be to inject EventBus into it's Command Handling method. That would works, however there is a better solution. Ecotone does provide possibility to automatically gather events from Aggregate and publish them using EventBus.

State-Stored Aggregate

To tell Ecotone, which method it should use for retrieving Event objects when using State-Stored Aggregate mark method containing events with annotation @AggregateEvents. After handling Command or Event on Aggregate events will be published.

/**
* @return object[]
* @AggregateEvents()
*/
public function getRecordedEvents() : array

If you do want to bother with implementation you can make use of trait WithAggregateEvents

use Ecotone\Modelling\Annotation\AggregateEvents;

If you want to record event for publication just use recordmethod.

$this->record(new OrderWasPlaced());

Events will be automatically retrieved and published after handling current Command or Event.

Event Sourcing Aggregate

When using Event Sourcing Aggregate you do not need to do anything extra. Each method should return events after handling, those events will automatically published using Event Bus.

public function assignWorker(AssignWorkerCommand $command) : array