prooph/event-sourcing
Lightweight PHP event sourcing library with out-of-the-box integration for prooph/event-store. Provides an AggregateRoot base and AggregateTranslator, plus UUID generation and assertions support. Note: project was supported until Dec 31, 2019 and is deprecated.
## Getting Started
### **First Steps**
1. **Installation**
```bash
composer require prooph/event-sourcing:^5.7.0
Add the package to your composer.json and run composer update. Note: This version requires PHP 8.0+ due to compatibility changes.
Core Concepts
OrderCreated, PaymentProcessed).prooph/event-store for persistence).Minimal Example (PHP 8 Syntax)
use Prooph\EventSourcing\AggregateRoot;
class Order extends AggregateRoot
{
public function create(string $orderId, float $amount): void
{
$this->recordThat(new OrderCreated($orderId, $amount));
}
}
AggregateRoot and use recordThat() to emit events.Replaying Events
$order = new Order();
$order->replay([new OrderCreated('123', 100.0)]);
Order, User).OrderCreated with orderId and amount).protected function apply(OrderCreated $event): void
{
$this->orderId = $event->orderId();
$this->amount = $event->amount();
// Use PHP 8's match expressions for complex event handling:
match ($event::class) {
OrderCreated::class => $this->handleOrderCreated($event),
OrderCancelled::class => $this->handleOrderCancelled($event),
};
}
prooph/event-store for persistence (ensure compatibility with PHP 8):
$eventStore = new \Prooph\EventStore\Pdo\MySqlEventStore(
$pdo,
new \Prooph\EventStore\Uuid\UuidGenerator()
);
$order->recordThat(new OrderCreated('123', 100.0));
$eventStore->appendToAggregate(
$order->aggregateId(),
$order->getUncommittedEvents(),
$order->expectedVersion()
);
prooph/service-bus for CQRS. Use PHP 8 attributes for metadata:
#[CommandHandler]
class CreateOrderHandler
{
public function __invoke(CreateOrderCommand $command, Order $order)
{
$order->create($command->orderId(), $command->amount());
}
}
prooph/event-store-bus-bridge to project events to read models:
$projection = new OrderProjection($eventStore, $bus);
$projection->listenTo(OrderCreated::class)->then(function (OrderCreated $event) {
// Update read model (e.g., database, cache)
});
$projection->listenTo(OrderCreated::class)->then(fn(OrderCreated $event) => {
// ...
});
$order = new Order();
$order->replay([new OrderCreated('123', 100.0)]);
$this->assertSame('123', $order->orderId());
InMemoryEventStore).
strict_types=1 in test files for type safety.PHP 8 Breaking Changes
create_function, call_user_func_array with variadic args).Event Ordering
prooph/event-store with PDO).Aggregate Identity
aggregateRootId() consistently with PHP 8's string type.State vs. Events
OrderTotal if recalculable).Concurrency (PHP 8 Optimistic Locking)
expectedVersion in appendToAggregate:
$eventStore->appendToAggregate(
$order->aggregateId(),
$order->getUncommittedEvents(),
$order->expectedVersion()
);
ConcurrentModificationException with PHP 8's exception handling:
try {
$eventStore->appendToAggregate(...);
} catch (ConcurrentModificationException $e) {
// Retry or notify user
}
Performance
match expressions for faster event routing in projections.$eventStore->setLogger(new \Monolog\Logger('event_store', [
'handlers' => [new \Monolog\Handler\StreamHandler('php://stderr')],
]));
var_dump or print_r:
var_dump($order->toArray());
EventValidator to catch malformed events early:
$validator = new EventValidator();
$validator->validate($event); // Throws \Prooph\Common\Exception\InvalidArgumentException
Custom Event Stores (PHP 8 Interfaces)
Prooph\EventStore\EventStoreInterface for custom backends (e.g., Kafka, DynamoDB):
class KafkaEventStore implements EventStoreInterface
{
public function appendToAggregate(string $aggregateId, array $events, int $expectedVersion = 0): void
{
// PHP 8: Use match expressions or strict typing
}
}
Event Serialization (PHP 8 Enums/Attributes)
EventSerializer for custom formats (e.g., JSON, Protocol Buffers):
class JsonEventSerializer implements EventSerializerInterface
{
public function serialize(array $events): string
{
return json_encode($events, JSON_THROW_ON_ERROR);
}
}
Aggregate Metadata (PHP 8 Properties)
aggregateMetadata() to store non-event data (e.g., timestamps):
$this->aggregateMetadata()->set('created_at', new \DateTimeImmutable());
Domain Events (PHP 8 Attributes)
OrderCreatedEvent) alongside aggregates:
$this->recordDomainEvent(new OrderCreatedEvent($event));
#[DomainEvent]
class OrderCreatedEvent {}
DateTimeImmutable.EventId for uniqueness (default: UUID v7 for PHP 8 compatibility).aggregateVersion to handle concurrent updates with PHP 8's strict typing.strict_types=1 in composer.json:
{
"config": {
"platform": {
"php": "8.0"
}
}
}
array_key_first()/`array_key_lastHow can I help you explore Laravel packages today?