January 10, 2025
· 4 min readGetting Familiar with Event Sourcing in Laravel
Event sourcing is a powerful architecture pattern for handling complex data changes over time. This blog will introduce you to the principles of event sourcing and demonstrate how to use it in your Laravel applications with the Spatie package.
Event sourcing is an architecture pattern where the state of a system is captured through a series of events rather than storing the current state directly in the database. This approach provides several benefits, such as full auditability, the ability to reconstruct past states, and improved scalability for certain types of applications.
In this blog, we’ll explore how to implement event sourcing in Laravel using the spatie/laravel-event-sourcing package. We’ll discuss key concepts, basic setup, and the different components involved in building an event-sourced system.
What is Event Sourcing?
Event sourcing is based on the idea that every change in an application’s state is triggered by an event. These events are stored in an append-only event store, which is a persistent log that records all state changes over time. By replaying these events, you can reconstruct the current state of an application, which eliminates the need for traditional database state models.
This pattern is particularly useful for systems with complex workflows, such as accounting systems, e-commerce platforms, and order management systems.
Additional Reading:
Key Components in Event Sourcing
Here are the key components of event sourcing:
-
Aggregates: Aggregates are the main entities in event sourcing. They represent the domain model and contain the business logic. Aggregates apply events to modify their internal state. They ensure that the system’s state is always consistent by ensuring that only valid events are applied.
-
Events: Events are immutable records of state changes that describe what has happened in the system. Once an event is created, it cannot be modified. Events are stored in an event store, and their sequence can be used to reconstruct the state of the system.
-
Projectors: Projectors create read models (projections) from events. These projections are optimized views of the data that can be used for querying. Projectors are responsible for transforming events into a form that’s suitable for consumption by the user interface or for reporting purposes.
-
Reactors: Reactors are responsible for handling side effects in response to events. For example, sending an email notification after a user creates an order or updating an external system when a payment event occurs. Reactors help decouple side effects from the main event logic.
Additional Reading:
Setting Up Event Sourcing in Laravel
To get started with event sourcing in Laravel, you need to install the spatie/laravel-event-sourcing package. This package makes it easy to set up and work with event sourcing in Laravel applications.
[[NEWSLETTER]]
-
Install the Package:
First, install the package via Composer:
composer require spatie/laravel-event-sourcing -
Create Your Event Class:
Next, you’ll create an event class to represent a change in your application. For example, if you're building a system to manage user accounts, you might have an event like
UserRegistered.namespace App\Events; use Spatie\EventSourcing\StoredEvents\ShouldBeStored; class UserRegistered extends ShouldBeStored { public $userId; public $email; public function __construct($userId, $email) { $this->userId = $userId; $this->email = $email; } } -
Create Your Aggregate:
Aggregates are where your business logic is stored. You define your aggregates by extending the
AggregateRootclass.namespace App\Aggregates; use Spatie\EventSourcing\AggregateRoots\AggregateRoot; use App\Events\UserRegistered; class UserAggregate extends AggregateRoot { public $userId; public $email; public function registerUser($userId, $email) { $this->recordThat(new UserRegistered($userId, $email)); } public function applyUserRegistered(UserRegistered $event) { $this->userId = $event->userId; $this->email = $event->email; } } -
Create a Projector:
Projectors are used to create read models for querying data. Here’s how you can set up a basic projector:
namespace App\Projectors; use App\Events\UserRegistered; use Spatie\EventSourcing\Projections\Projection; class UserProjector extends Projection { public function onUserRegistered(UserRegistered $event) { // Store the user in a projection (e.g., a database table for fast queries) \DB::table('users')->insert([ 'user_id' => $event->userId, 'email' => $event->email, ]); } } -
Reactors and Event Handlers:
Reactors are used for handling side effects of events. For example, if you want to send an email when a user registers, you can use a reactor:
namespace App\Reactors; use App\Events\UserRegistered; use Illuminate\Support\Facades\Mail; class UserReactor { public function onUserRegistered(UserRegistered $event) { Mail::to($event->email)->send(new \App\Mail\WelcomeMail()); } }
Additional Reading:
Benefits of Event Sourcing
Event sourcing provides several advantages for certain use cases:
- Auditability: Since all state changes are stored as events, you can easily track the history of changes.
- Reconstructing State: You can rebuild the state of an application at any point in time by replaying events.
- Scalability: Event sourcing is well-suited for distributed systems and can help you scale your application efficiently.
Additional Reading:
Conclusion
Event sourcing can be a powerful pattern for handling state changes in complex applications. By using Spatie’s laravel-event-sourcing package, you can easily integrate this architecture into your Laravel applications. Whether you’re building a system with complex business logic or need a reliable audit trail, event sourcing offers a flexible and scalable solution.
For more information on setting up event sourcing in your Laravel app, refer to the official Spatie documentation here.