RabbitMQ event sourcing for Laravel/Lumen microservices
Foreword
Over the past week, I was studying about microservices and event-driven architecture. I decided to go with the event sourcing paradigm since it looked promising when it is executed right. I am using Lumen and I stumbled into the beautiful spatie/laravel-event-sourcing
package.
I loved its developer experience and such. It made me much closer to event sourcing. I was wondering if its ShouldStore
events could be enqueued into a RabbitMQ queue. I can achieve something primitively by declaring my projectors to use the ShouldQueue
interface. But I want one or many more microservices to receive the events!
I researched some more. I then luckily discovered the nuwber/rabbitevents
package. It is a simple observer implementation for events published in RabbitMQ. It leverages topic exchange hence one or many microservices can listen to an event; given an appropriate event (or routing) key. It worked beautifully as to what I exactly needed.
Solution
I decided to make a simple package that couples the two together, thereby leveraging event sourcing and intraservice communication using RabbitMQ. I decided to call the package laravel-rabbitevents-sourcing
(how convenient). Check out its GitHub repository here .
I created the package so I would reduce the code redundancies among our microservices. I do not want to keep writing the abstract classes and helper functions over and over again, just so I can integrate the two. It also opens an avenue for me to write more possibly integrated functionalities in the future.
Installation
Install the package using composer. Afterwards, the abstractions and helper functions are immediately made available to you!
$ composer require jozi/laravel-rabbitevents-sourcing
Usage/Examples
All stored and published events extends the StoredRabbitEvent
class. These events will be handled for both event sourcing (spatie/laravel-event-sourcing
) and publishing to RabbitMQ (nuwber/rabbitevents
).
For these StoredRabbitEvent
events, a string $eventKey
is explictly required for the event to be published. The $eventKey
is the same as RabbitMQ's routing key.
use Jozi\Events\StoredRabbitEvent;
class AccountCreated extends StoredRabbitEvent
{
public $eventKey = 'account.created';
/** @var array */
public $accountAttributes;
public function __construct(array $accountAttributes)
{
$this->accountAttributes = $accountAttributes;
}
public function toPublish(): array
{
return $this->accountAttributes;
}
}
After an event has been defined, you may invoke it using the publish_event
helper function. It is just a simple wrapper for invoking both event
and publish
as a one-liner.
class Account extends Model
{
protected $guarded = [];
public static function createWithAttributes(array $attributes): Account
{
/*
* Let's generate a uuid.
*/
$attributes['uuid'] = (string) Uuid::uuid4();
/*
* The account will be created inside this event using the generated uuid.
*/
publish_event(new AccountCreated($attributes));
/*
* The uuid will be used the retrieve the created account.
*/
return static::getByUuid($attributes['uuid']);
}
}
And then, that is it! Configure your Listener
classes in your RabbitEventsServiceProvider
to start eavesdropping on some events.
Read more on nuwber/rabbitevents
documentation to make sure your setup is correct.
Acknowledgements
- nuwber/rabbitevents - package used to publish events into RabbitMQ queues through topic exchange
- spatie/laravel-event-sourcing - package used to achieve event sourcing