Files
michaelschiemer/src/Framework/EventSourcing/Snapshots/SnapshotableAggregate.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

139 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Snapshots;
use App\Framework\EventSourcing\AggregateId;
use App\Framework\EventSourcing\AggregateRoot;
/**
* Snapshotable Aggregate Helper
*
* Extends AggregateRoot functionality with snapshot support
* without modifying the core AggregateRoot class
*/
final readonly class SnapshotableAggregate
{
public function __construct(
private SnapshotStore $store,
private SnapshotStrategy $strategy
) {
}
/**
* Load aggregate with snapshot optimization
*/
public function load(
AggregateId $aggregateId,
callable $eventStreamLoader,
callable $aggregateFactory
): AggregateRoot {
// Try to load latest snapshot
$snapshot = $this->store->getLatest($aggregateId);
if ($snapshot) {
// Load from snapshot + events since snapshot
$eventsSinceSnapshot = $eventStreamLoader($snapshot->getVersion());
$state = $this->reconstructState($snapshot);
return AggregateRoot::rehydrate(
$aggregateId,
$state,
$eventsSinceSnapshot
);
}
// No snapshot - load all events
$events = $eventStreamLoader(0);
return $aggregateFactory($aggregateId, $events);
}
/**
* Save aggregate and create snapshot if strategy says so
*/
public function saveWithSnapshot(AggregateRoot $aggregate): void
{
// Check if snapshot should be taken
if ($this->strategy->shouldTakeSnapshot($aggregate)) {
$snapshot = $this->createSnapshot($aggregate);
$this->store->save($snapshot);
}
}
/**
* Force snapshot creation
*/
public function forceSnapshot(AggregateRoot $aggregate): Snapshot
{
$snapshot = $this->createSnapshot($aggregate);
$this->store->save($snapshot);
return $snapshot;
}
/**
* Get aggregate version from event recorder
*/
public function getVersion(AggregateRoot $aggregate): int
{
return $aggregate->eventRecorder->getVersion();
}
/**
* Create snapshot from aggregate
*/
private function createSnapshot(AggregateRoot $aggregate): Snapshot
{
return Snapshot::take(
aggregateId: $aggregate->id,
version: $this->getVersion($aggregate),
state: $this->extractState($aggregate),
aggregateClass: get_class($aggregate->state)
);
}
/**
* Extract state array from aggregate
*/
private function extractState(AggregateRoot $aggregate): array
{
// Use reflection to extract state properties
$reflection = new \ReflectionObject($aggregate->state);
$state = [];
foreach ($reflection->getProperties() as $property) {
$property->setAccessible(true);
$value = $property->getValue($aggregate->state);
// Serialize Value Objects and complex types
if (is_object($value)) {
if (method_exists($value, 'toArray')) {
$state[$property->getName()] = $value->toArray();
} elseif (method_exists($value, 'toString')) {
$state[$property->getName()] = $value->toString();
} else {
$state[$property->getName()] = serialize($value);
}
} else {
$state[$property->getName()] = $value;
}
}
return $state;
}
/**
* Reconstruct state object from snapshot
*/
private function reconstructState(SnapshotInterface $snapshot): object
{
$stateClass = $snapshot->getAggregateClass();
$stateData = $snapshot->getState();
// Simple reconstruction - would need enhancement for complex VOs
return new $stateClass(...$stateData);
}
}