$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[February 05, 2026] Laravel

Laravel Event Sourcing: Basics

Laravel Event Sourcing: Basics

────────────────────────────────────────────────────────

Laravel Event Sourcing: Basics

Tip: Store Events, Not State

event(new OrderCreated($orderId, $items));
event(new OrderPaid($orderId, $amount));

Each event represents a state change.

Gotcha: Events Are Immutable

Once stored, events should never be modified. Append new events to correct mistakes.

Tip: Projectors Rebuild State

class OrderProjector
{
    public function onOrderCreated(OrderCreated $event): void
    {
        Order::create(['id' => $event->orderId, 'status' => 'created']);
    }
}

Gotcha: Event Ordering Matters

Events must be processed in the order they occurred. Use timestamps or sequence numbers.

Tip: Snapshots for Performance

$snapshot = OrderSnapshot::where('order_id', $id)->latest()->first();
$events = Event::where('order_id', $id)->after($snapshot->sequence)->get();

Skip replaying all events by starting from a snapshot.

Gotcha: Event Versioning

Include a version field in events to handle schema evolution over time.

Tip: Use route:cache Carefully

php artisan route:cache is fast, but it doesn't work with closure-based routes. Every time you cache routes, Laravel serializes them. If you have Route::redirect() or closure callbacks, the cache breaks. Stick to controller-based routes in production.

Tip: Model APP_KEY Rotation

Rotating APP_KEY invalidates all encrypted data — cookies, encrypted DB columns, and password reset tokens. If you must rotate (e.g., after a leak), plan a migration that re-encrypts existing data with the new key.

Gotcha: Local Scope Leaks

Global scopes defined in booted() apply to ALL queries on that model — including relationships. An innocent User::all() in admin panel might exclude soft-deleted users if a global scope is active.

Senior Insight

Event sourcing is a powerful pattern, but it's overkill for most applications. I've consulted on projects where the team implemented event sourcing because it sounded cool, only to realize they needed simple audit logging. If you don't need to rebuild state from events or query historical states, you probably don't need event sourcing. Start with Laravel's native audit features and only adopt event sourcing when the requirements explicitly demand it.

Source: Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/laravel), Spatie Blog (https://spatie.be/blog)

────────────────────────────────────────────────────────
<-- back to posts