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