A fully interactive Laravel application demonstrating Event Sourcing using Spatie Event Sourcing package. All Event Sourcing functionality is accessible through the browser interface - no terminal commands needed for everyday use.
- Interactive Livewire UI - All functionality available through browser
- Event Sourcing - All state changes stored as immutable events
- Event Replay - Rebuild entire application state from event history (via browser button)
- Time Travel - Navigate through event history step-by-step and view historical application states
- Event History Viewer - View all events for any todo item
- Clear Projection - Test event replay by clearing and rebuilding the read model
- IP-based Limits - Maximum 3 active todos per IP address per hour
- Real-time Statistics - Track number of events and todos in real-time
- Restore Deleted Todos - Soft delete with ability to restore
- Modern UI - Clean Tailwind CSS interface
- Code Quality Tools - Built-in Pint and Larastan integration via
php artisan qa
- Laravel 12
- Livewire 3
- Spatie Event Sourcing
- SQLite
- Tailwind CSS
# Install dependencies (if needed)
composer install
# Setup database
php artisan migrate:fresh --seed
# Build read model from events
php artisan todo:replay
# Start server
php artisan serveOpen browser at: http://127.0.0.1:8000
- Enter todo title in the input field
- Click "Add Todo" button
- Todo is created and
TodoCreatedevent is stored
- Click "Complete" button on any pending todo
- Status changes to completed
TodoCompletedevent is stored
- Click "Reopen" button on any completed todo
- Status reverts to pending
TodoReopenedevent is stored
- Click "Delete" button on any todo
- Confirm deletion
- Status changes to deleted (soft delete)
TodoDeletedevent is stored
- Deleted todos appear in trash section
- Click "Restore" button on any deleted todo
- Status reverts to its previous state (pending/completed)
TodoRestoredevent is stored
- Click "Event History" link on any todo
- Modal opens showing all events for that todo
- View event types, timestamps, and JSON data
- Click "Replay All Events" button in sidebar
- Application performs:
- Truncates the
todostable (read model) - Loads all events from
stored_eventstable - Replays them through the projector
- Reconstructs complete application state
- Truncates the
This demonstrates the power of Event Sourcing - you can rebuild your entire application state from the event log at any time.
- Click "Clear Projection" button
- All todos disappear from the read model
- Events remain safe in the event store
- Use "Replay Events" to restore everything
- Click "Time Travel" button in sidebar
- Modal opens with slider control
- Drag slider to navigate through event history
- Watch todos change to reflect historical state
- Use step forward/backward buttons for precise navigation
- Application enters read-only mode during time travel
- Click "Exit Time Travel" to return to live state
- All modifications are blocked while viewing historical snapshots
Time Travel Features:
- Slider control for navigating all events
- Real-time preview of historical state
- Read-only mode prevents accidental modifications
- Visual indicator showing current event position
- Step-by-step navigation buttons
Events
TodoCreated- New todo created with title and IPTodoCompleted- Todo marked as completedTodoReopened- Completed todo reopenedTodoDeleted- Todo soft deletedTodoRestored- Deleted todo restored to previous state
Aggregate
TodoAggregate- Aggregate Root handling business logic and recording events
Projector
TodoProjector- Listens to events and updates thetodosread model table
Commands
CreateTodo- Creates new todo and records eventCompleteTodo- Marks todo complete and records eventReopenTodo- Reopens todo and records eventDeleteTodo- Deletes todo and records eventRestoreTodo- Restores deleted todo and records event
Read Model
Todo- Eloquent model representing the projection (todos table)
Enum
TodoStatus- pending, completed, deleted
User Action (Browser)
↓
Livewire Component Method
↓
Command Handler
↓
Aggregate Root (records event)
↓
Event Store (stored_events table)
↓
Projector (handles event)
↓
Read Model (todos table)
↓
Livewire Re-render
↓
Browser Update
app/
├── Aggregates/
│ └── TodoAggregate.php # Aggregate Root with business logic
├── Commands/
│ ├── CreateTodo.php
│ ├── CompleteTodo.php
│ ├── ReopenTodo.php
│ ├── DeleteTodo.php
│ └── RestoreTodo.php
├── Console/Commands/
│ ├── TodoReplayCommand.php # php artisan todo:replay
│ ├── TodoRollbackCommand.php # php artisan todo:rollback {uuid}
│ └── CheckCodeQualityCommand.php # php artisan qa
├── Enums/
│ └── TodoStatus.php # pending | completed | deleted
├── Events/
│ ├── TodoCreated.php
│ ├── TodoCompleted.php
│ ├── TodoReopened.php
│ ├── TodoDeleted.php
│ └── TodoRestored.php
├── Livewire/
│ └── TodoList.php # Main Livewire component
├── Models/
│ └── Todo.php # Read model (projection)
└── Projectors/
└── TodoProjector.php # Event handlers updating read model
database/
├── migrations/
│ ├── create_todos_table.php
│ ├── create_stored_events_table.php
│ └── create_snapshots_table.php
└── seeders/
└── TodoSeeder.php # Sample data (5 todos, 7 events)
id- Primary keyuuid- Aggregate UUID (links to events)title- Todo titlestatus- Current status (pending/completed/deleted)ip- IP address of creatortimestamps
id- Auto incrementaggregate_uuid- Links to todos.uuidevent_class- Fully qualified event class nameevent_properties- JSON serialized event datameta_data- Additional metadatacreated_at- Event timestamp
php artisan todo:replayTruncates the todos table and rebuilds it from all stored events. Demonstrates event replay capability.
php artisan todo:rollback {uuid}Removes the last event for a specific todo UUID and replays remaining events to restore correct state.
php artisan qaRuns Laravel Pint (code style fixer) and Larastan (static analysis) to ensure code quality. This command:
- Automatically fixes code style issues with Pint
- Runs PHPStan/Larastan static analysis
- Returns success/failure status
- Create several todos through the UI
- Complete some of them
- Click "Event History" on any todo
- Observe all events with timestamps
- Create multiple todos with different statuses
- Note the current state
- Click "Clear Projection" - all todos disappear
- Click "Replay All Events" - everything returns exactly as it was
- This proves the event store is the source of truth
- Create 3 todos
- Try to create a 4th todo within the same hour
- Receive error: "Maximum 3 todos per IP per hour allowed"
- Wait one hour OR delete one todo
- Now you can create another
- Create multiple todos and perform various actions (complete, reopen, delete)
- Click "Time Travel" button
- Drag slider to earlier event (e.g., event #3)
- Observe state exactly as it was at that point in time
- Try to create a new todo - blocked with read-only message
- Navigate forward/backward through events
- Click "Exit Time Travel" to return to live mode
- Delete a todo
- Todo moves to trash section
- Click "Restore" button
- Todo returns to its previous state (pending or completed)
- View event history to see
TodoRestoredevent recorded
Immutability
- Events are never modified or deleted
- New events are added to represent state changes
- Complete audit trail of all changes
Event Replay
- Read model can be completely rebuilt from events
- Enables time travel and state reconstruction at any point in history
- Step-by-step navigation through all historical states
- Protects against data corruption
CQRS (Command Query Responsibility Segregation)
- Write model: Commands → Aggregates → Events
- Read model: Events → Projectors → Database Table
- Separate concerns for writing and reading data
Aggregates
TodoAggregateenforces business rules- Records events representing state changes
- Prevents invalid state transitions
Projections
TodoProjectorbuilds queryable read model- Can have multiple projections from same events
- Optimized for specific query patterns
The browser interface provides:
- Todo list with status badges (Pending/Completed/Deleted)
- Add, Complete, Reopen, Delete, and Restore buttons
- Trash section showing deleted todos with restore capability
- Event History modal for each todo showing all events with timestamps
- Time Travel interface with:
- Interactive slider for navigating event history
- Step forward/backward buttons
- Read-only mode indicator during time travel
- Current event position display
- Event Sourcing control panel with:
- Time Travel button
- Replay All Events button
- Clear Projection button
- Real-time statistics (event count, todo count)
- Success/error flash messages
- IP address display
- Total events counter
- Create event class in
app/Events - Add method to
TodoAggregateto record it - Add handler method to
TodoProjector - Create command class if needed
- Wire up in Livewire component
The TodoProjector can be modified to:
- Update multiple tables
- Trigger notifications
- Update search indexes
- Generate analytics
While the browser interface provides event replay, you can also rollback individual todos using the console:
php artisan todo:rollback {uuid}This removes the last event for the specified todo and replays remaining events.
The application includes fully functional API routes with rate limiting:
- GET
/api/todos- List todos - POST
/api/todos- Create todo - POST
/api/todos/{uuid}/toggle- Toggle status - DELETE
/api/todos/{uuid}- Delete todo
Rate limit: 10 requests per minute
All API endpoints support both JSON and standard responses. Example:
curl -X POST http://127.0.0.1:8000/api/todos \
-H "Content-Type: application/json" \
-d '{"title": "My new todo"}'No todos appear after seed
php artisan todo:replayEvents not projecting
Check that TodoProjector method names match event class names:
handleTodoCreatedforTodoCreatedeventhandleTodoCompletedforTodoCompletedeventhandleTodoReopenedforTodoReopenedeventhandleTodoDeletedforTodoDeletedeventhandleTodoRestoredforTodoRestoredevent
Database errors
php artisan migrate:fresh --seed
php artisan todo:replayCode quality issues
php artisan qaThis will automatically fix code style issues and run static analysis.
Built with Spatie Laravel Event Sourcing
MIT