Implementing Event Sourcing in Laravel 11 for Audit Trails

In this article, I'll walk you through implementing event sourcing in Laravel 11 to create a reliable audit trail for business-critical applications. Event sourcing allows us to capture every change in an application's state as an event, storing it as an immutable record.

This approach makes it easier to track changes, provide a full history, and even rebuild the state of an application from the stored events. Let’s dive into how we can build this with Laravel 11.

Implementing Event Sourcing in Laravel 11 for Audit Trails

Implementing Event Sourcing in Laravel 11 for Audit Trails

 

Step 1: Install Laravel Event Sourcing Package

The first step is to install the Laravel Event Sourcing package. This package provides tools for working with event sourcing in Laravel.

composer require spatie/laravel-event-sourcing

Once installed, publish the package configuration file:

php artisan vendor:publish --provider="Spatie\EventSourcing\EventSourcingServiceProvider" --tag="event-sourcing-config"

This will generate a config/event-sourcing.php file where you can customize the settings.

 

Step 2: Create an Event

Next, we’ll create an event that captures the changes in your application. Let’s say we are tracking changes to a user's profile.

php artisan make:event UserProfileUpdated

In the generated event class (app/Events/UserProfileUpdated.php), define the properties of the event.

namespace App\Events;

use Spatie\EventSourcing\StoredEvents\ShouldBeStored;

class UserProfileUpdated extends ShouldBeStored
{
    public $userId;
    public $changes;

    public function __construct(int $userId, array $changes)
    {
        $this->userId = $userId;
        $this->changes = $changes;
    }
}

Here, ShouldBeStored ensures that this event is stored in the event store.

 

Step 3: Record Events in the Event Store

Now, whenever a user's profile is updated, we’ll record the event. In your controller or service, trigger the event like this.

use App\Events\UserProfileUpdated;

public function updateProfile(Request $request, $id)
{
    $user = User::find($id);
    $changes = $request->only(['name', 'email', 'address']);
    
    $user->update($changes);

    // Fire the event
    event(new UserProfileUpdated($user->id, $changes));
    
    return response()->json(['message' => 'Profile updated successfully.']);
}

This will store the UserProfileUpdated event with the user's changes in the event store.

 

Step 4: Rebuild State from Events

One of the key benefits of event sourcing is the ability to rebuild an entity’s state by replaying stored events. To handle this, we need a projector that listens to our events and updates the read model.

Create a projector using the following artisan command:

php artisan make:projector UserProfileProjector

In the projector (app/Projectors/UserProfileProjector.php), define how to apply the event to rebuild the user's profile.

namespace App\Projectors;

use App\Events\UserProfileUpdated;
use Spatie\EventSourcing\EventHandlers\Projectors\Projector;

class UserProfileProjector extends Projector
{
    public function onUserProfileUpdated(UserProfileUpdated $event)
    {
        $user = User::find($event->userId);
        $user->update($event->changes);
    }
}

This projector listens for UserProfileUpdated events and updates the user’s profile accordingly.

 

Step 5: Enable Event Projection

To ensure our projectors are handling the events, we need to register them. Open the config/event-sourcing.php file and add your projector under the projectors array.

'projectors' => [
    \App\Projectors\UserProfileProjector::class,
],

Finally, run the event projections by using the following command.

php artisan event-sourcing:replay

This command will replay all stored events and apply them to rebuild the application state.

 

Step 6: Setting Up Audit Trail

Event sourcing inherently provides a detailed audit trail by storing all events. You can query the stored events for auditing purposes.

To fetch all events related to a specific user, use the StoredEvent model:

use Spatie\EventSourcing\StoredEvents\Models\EloquentStoredEvent;

$events = EloquentStoredEvent::where('event_class', UserProfileUpdated::class)
            ->where('event_properties->userId', $userId)
            ->get();

foreach ($events as $event) {
    // Access event properties
    $eventData = $event->event_properties;
    echo "User ID: " . $eventData['userId'] . " | Changes: " . json_encode($eventData['changes']);
}

This will list all the changes made to a user’s profile, providing a full audit trail.

 


You might also like:

techsolutionstuff

Techsolutionstuff | The Complete Guide

I'm a software engineer and the founder of techsolutionstuff.com. Hailing from India, I craft articles, tutorials, tricks, and tips to aid developers. Explore Laravel, PHP, MySQL, jQuery, Bootstrap, Node.js, Vue.js, and AngularJS in our tech stack.

RECOMMENDED POSTS

FEATURE POSTS