Overview Traditional applications focus on the "now." You have a database table, a row, and a column—like a player's score—and you update that value as things change. But when you overwrite data, you lose the history of how you got there. Verbs is an event sourcing library for Laravel that flips this script. Instead of storing the current state, you store the actions (the "verbs") that led to that state. This matters because it provides a perfect audit trail and the ability to "time travel." If you discover a bug in your logic, you don't just fix the code for future users; you can actually replay the entire history of your application through the corrected logic to fix the past. It transforms data from a static snapshot into a living narrative. Prerequisites To follow this guide, you should be comfortable with: * **PHP 8.x** and the Laravel framework. * **Eloquent Models**: Understanding how traditional CRUD works. * **Livewire**: Familiarity with basic component structure and method firing. * **Terminal/CLI**: Basic knowledge of running Artisan commands. Key Libraries & Tools * Verbs: The core event sourcing package for Laravel. * Laravel Livewire: Used for the reactive frontend components. * Laravel Forge: The deployment tool used for the production demonstration. The Architecture: States and Events In Verbs, there are only two primary concepts you need to grasp: **States** and **Events**. States A State is a simple PHP object representing your data at a specific moment. It is the "noun" of your application. Unlike Eloquent models, states are derived from events. ```php class PlayerState extends State { public int $score = 0; } ``` Events Events are immutable records of things that happened. They contain the data needed to change the state. Once an event is fired, it is written to the database and never changed. ```php class Upvoted extends Event { public int $playerId; public function applyToPlayer(PlayerState $state) { $state->score++; } } ``` Code Walkthrough: Implementing a Secure Event Let’s look at a practical scenario: playing a secret code to get points. In a standard app, you might just increment a score in a controller. In Verbs, we handle the validation and the state mutation within the event itself. 1. The Livewire Trigger First, we trigger the event from a Livewire component. We pass the necessary IDs and the code the user entered. ```php public function submitCode() { PlayedCode::fire( playerId: $this->playerId, gameId: $this->gameId, code: $this->code ); } ``` 2. Validation Logic Events should decide if they are allowed to happen. We use a `validate` method to check the `GameState`. If the code isn't in the valid collection, we throw an exception. ```php public function validate(GameState $game) { $this->assert( $game->codes->contains($this->code), "The code {$this->code} is not valid." ); } ``` 3. Mutating Multiple States A single event can change multiple states. Here, we increment the player's score and remove the used code from the game state simultaneously. ```php public function applyToPlayer(PlayerState $player) { $player->score++; } public function applyToGame(GameState $game) { $game->codes = $game->codes->reject(fn($c) => $c === $this->code); } ``` Syntax Notes * **Dependency Injection**: Notice how the `apply` and `validate` methods type-hint the state objects. Verbs automatically resolves the correct state instance based on the IDs provided in the event. * **Method Naming**: The convention `applyTo{StateName}` allows an event to target specific states without complex configuration. * **Immutability**: Once `PlayedCode` is stored in the `verbs_events` table, the data properties cannot be changed. This is your source of truth. Practical Examples Event sourcing excels in environments where auditability is non-negotiable: * **Financial Systems**: You don't just want a balance; you want every transaction that led to it. * **Gaming**: As seen in the Thunk pyramid scheme, it prevents cheating by re-verifying every move against the rules. * **Security Audits**: You can store IP addresses in event metadata. If an attacker breaches an account, you can replay the stream while specifically ignoring any events originating from the attacker's IP. Tips & Gotchas * **The Replay Power**: If you discover that users were exploiting a bug (like reusing the same code because you forgot the `applyToGame` logic), you don't need a complex migration. You fix the code, truncate your state tables, and run `php artisan verbs:replay`. The system will re-process every event through the new, corrected logic. * **Performance**: Replaying thousands of events is surprisingly fast, but for massive datasets, Verbs uses "snapshots" to cache state at certain points, preventing the need to boot from event zero every time. * **State vs. Database**: Remember that while states are stored in the database for performance (snapshots), the `verbs_events` table is the only part of your database that truly matters for long-term integrity.
Laravel Livewire
Products
- Sep 9, 2024
- Jun 13, 2024