surgio/eloquent-message-repository
Installation
composer require surgio/eloquent-message-repository:^4.0
Publish Migrations
php artisan vendor:publish --provider="Surgio\EloquentMessageRepository\EventSauceServiceProvider" --tag="migrations"
Run the migration:
php artisan migrate
Basic Setup in a Service
Register the repository in your EventSauce configuration (e.g., in a service provider or DI container):
use Surgio\EloquentMessageRepository\EloquentMessageRepository;
use EventSauce\EventSourcing\MessageSerializer\ConstructingMessageSerializer;
$messageRepository = new EloquentMessageRepository(new ConstructingMessageSerializer());
First Use Case: Storing Events
Pass the repository to an AggregateRootRepository (e.g., ConstructingAggregateRootRepository):
use EventSauce\EventSourcing\AggregateRootRepository\ConstructingAggregateRootRepository;
$aggregateRootRepository = new ConstructingAggregateRootRepository(
YourAggregate::class,
$messageRepository
);
Event Storage
The repository automatically persists events to the messages table (created by the migration). Each event is stored with:
aggregate_id (UUID)aggregate_type (class name)event_name (FQCN of the event)event_data (serialized payload)event_metadata (optional metadata)occurred_on (timestamp)Event Retrieval Fetch events for an aggregate using:
$events = $messageRepository->getEventsFor(
aggregateId: $aggregateId,
aggregateType: YourAggregate::class
);
Integration with Laravel Services Bind the repository to the container in a service provider:
$this->app->bind(
EventSauce\EventSourcing\MessageRepository::class,
fn($app) => new EloquentMessageRepository(
new ConstructingMessageSerializer()
)
);
Event Sauce Process Setup Configure a process with the repository:
use EventSauce\EventSourcing\AggregateRootRepository\ConstructingAggregateRootRepository;
use EventSauce\EventSourcing\ProcessManager\SynchronousProcessManager;
$processManager = new SynchronousProcessManager(
new ConstructingAggregateRootRepository(
YourAggregate::class,
$messageRepository
)
);
Custom Serialization
Override the default ConstructingMessageSerializer for custom event serialization:
$serializer = new YourCustomSerializer();
$messageRepository = new EloquentMessageRepository($serializer);
Query Scoping Extend the repository to add custom query scopes (e.g., for soft-deleted events):
class CustomEloquentMessageRepository extends EloquentMessageRepository
{
public function scopeForProcess($query, string $processName)
{
return $query->where('process_name', $processName);
}
}
Event Metadata Handling Attach metadata to events during dispatch:
$event = new YourEvent($data);
$event->setMetadata(['user_id' => auth()->id()]);
Bulk Operations Use Laravel’s query builder for bulk inserts (e.g., during batch processing):
DB::table('messages')->insert($events);
Migration Conflicts
messages table already exists, manually inspect the migration to avoid schema conflicts.aggregate_id is a UUID-compatible column (e.g., uuid type in PostgreSQL or char(36) in MySQL).Serialization Issues
EventSauce\EventSourcing\Message. Non-compliant events will throw exceptions.#[Spatie\LaravelIgnition\Ignition] or similar tools to debug.Performance with Large Aggregates
$events = $messageRepository->getEventsFor($aggregateId, YourAggregate::class, 100, 0);
Concurrency Control
version column to the messages table if needed and implement checks in your aggregates.Query Logging Enable Laravel’s query logging to inspect SQL:
DB::enableQueryLog();
$messageRepository->getEventsFor($aggregateId, YourAggregate::class);
dd(DB::getQueryLog());
Event Validation Validate event data before dispatching:
$event->validate(); // If using Laravel validation traits
Repository Testing Mock the repository in tests to isolate aggregate logic:
$mockRepo = Mockery::mock(EloquentMessageRepository::class);
$mockRepo->shouldReceive('getEventsFor')
->andReturn([$event1, $event2]);
Custom Event Table Override the table name in the repository constructor:
$messageRepository = new EloquentMessageRepository(
new ConstructingMessageSerializer(),
'custom_event_table'
);
Event Filtering Extend the repository to filter events by metadata or other criteria:
class FilteredEloquentMessageRepository extends EloquentMessageRepository
{
public function getEventsForWithMetadata($aggregateId, $aggregateType, $metadataKey, $metadataValue)
{
return $this->newQuery()
->where('aggregate_id', $aggregateId)
->where('aggregate_type', $aggregateType)
->whereJsonContains('event_metadata->>' . $metadataKey, $metadataValue)
->get();
}
}
Event Sauce Middleware
Integrate with EventSauce middleware for cross-cutting concerns (e.g., logging, auditing):
use EventSauce\EventSourcing\Middleware\Middleware;
$middleware = new YourMiddleware();
$processManager = new SynchronousProcessManager(
$aggregateRootRepository,
[$middleware]
);
Laravel Events
Dispatch Laravel events alongside EventSauce events for UI updates:
event(new YourLaravelEvent($eventData));
How can I help you explore Laravel packages today?