Laravel events without bindings. Freedom for your architecture.
Performing actions directly where things happen is not always the best way. Take a controller, for example. If you update a model in the controller, you could later do other things that are defined by your business logic, like adding some logging. Doing so in the controller might work, but what if the model is updated by another service?
Events with listeners or observers (aka the listener pattern) is a better way to do this. You can implement and register
a LoggingObserver with a saved method for example. This method will then do the logging - even if the model
is updated elsewhere. The code is a bit less coupled than in the controller, but it is still coupled to the model and
laravel namespace.
But what if you plan to make it even a bit more loosely coupled? Maybe you even think about eliminating the direct implementation of the laravel namespace? Or using a microservice to perform the logging? This is where the BoundLess package comes in.
The package aims to abstract the laravel internal listener pattern in two ways — with a little addon.
- the event name is not tied to a laravel class
- the payload is not tied to a laravel class
- BONUS: you could define how the event will be dispatched
Making it possible to implement a custom dispatcher, the limits are not within your laravel app, it is boundless. Think of emitting an event to a microservice via webhook.
Simply install the package via composer, the package uses some laravel classes, but it has no config, migrations or even a service provider.
composer require mcgo/laravel-boundless
Wherever you want to send an event, you can use the McGo\BoundLess\Services\Boundless class. It feels like a
facade when using it:
use McGo\BoundLess\Services\BoundLess;
use App\Integration\BoundLess\DomainEventDefinition
BoundLess::init()
    ->forDefinition(DomainEventDefinition::class)
    ->dispatch();
This will use the App\Integration\BoundLess\DomainEventDefinition from your app to dispatch the event.
Each event that you want to send should have a class that implements the McGo\BoundLess\Contracts\ABoundLessEventDefinition
interface. The definition has two static functions, which define what event name will be propagated and how it will be
dispatched. This could be in your app:
<?php
namespace App\Integration\BoundLess;
use McGo\BoundLess\Contracts\ABoundLessEventDefinition;
use McGo\BoundLess\Dispatchers\BoundLessEventDispatcher;
class DomainEventDefinition implements ABoundLessEventDefinition
{
    public static function eventName(): string
    {
        return 'domain.event';
    }
    
    public static function getDispatcher(): string
    {
         return BoundLessEventDispatcher::class; 
    }
}
The event dispatcher is where the magic happens. The package comes with the BoundLessEventDispatcher class, which
simply sends a laravel event with the name McGo\BoundLess\Events\BoundLessEvent, containing the event name and
payload. It is a good idea to create your own dispatcher, so you can implement your own logic for dispatching the event.
Maybe you want to send the event to a microservice via webhook? See my package laravel-boundless-webhook
for an example - or directly use it.
Since the event payload should also be boundless, the BoundLess service has the method ->withPayload() which takes
a class that implements the McGo\BoundLess\Contracts\ABoundLessPayloadDTO interface. All you have to do is implement
the toPayloadArray() method, which should return an array.
For an example see the test class MockedEventPayload in this package.
A Facade is a handy singleton in your laravel app - a class that is instantiated once during the request cycle and can later be accessed like a class with static methods. Since every BoundLess call will use a different event definition and payload, it is really unnecessary to use a facade. But. The fluent interface of not "newing" the BoundLess class is nice and I decided to use the static init() method to be able to use method chaining afterwards.