Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
use App\Framework\Core\ValueObjects\Timestamp;
final readonly class AggregateEnvelope
{
public function __construct(
public AggregateId $aggregateId,
public object $event,
public int $version,
public Timestamp $recordedAt,
public EventMetadata $metadata,
) {
}
public function eventName(): string
{
return get_class($this->event);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
final readonly class AggregateId
{
public function __construct(
private string $id
) {
}
public function __toString(): string
{
return $this->id;
}
public static function generate(): self
{
return new self(uniqid('aggregate_', true));
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
final readonly class AggregateRoot
{
public DefaultEventRecorder $eventRecorder;
public function __construct(public object $state, public AggregateId $id, int $version = 0)
{
$this->eventRecorder = new DefaultEventRecorder($version);
}
public static function rehydrate(AggregateId $aggregateId, object $state, iterable $events): self
{
$self = new self($state, $aggregateId);
foreach ($events as $event) {
$self->apply($event, false);
}
return $self;
}
public function applyAndRecord(object $event): void
{
$this->apply($event, true);
}
private function apply(object $event, bool $record): void
{
$method = 'when' . new \ReflectionClass($event)->getShortName();
$ref = new \ReflectionObject($this->state);
if (! $ref->hasMethod($method)) {
throw new \RuntimeException("Missing method $method in state " . $ref->getName());
}
$ref->getMethod($method)->invoke($this->state, $event);
if ($record) {
$this->eventRecorder->record($event);
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\Clock;
final readonly class AggregateRootRepository
{
public function __construct(
private EventStore $eventStore,
private Clock $clock,
) {
}
public function load(AggregateId $aggregateId, object $state): AggregateRoot
{
$events = $this->eventStore->loadStream($aggregateId);
return AggregateRoot::rehydrate(
aggregateId: $aggregateId,
state: $state,
events: $events
);
}
public function save(AggregateRoot $root): void
{
$events = $root->eventRecorder->pullRecordedEvents();
$ae = new AggregateEnvelope(
aggregateId: $root->id,
event : $events[0],
version : $root->eventRecorder->version,
recordedAt : Timestamp::fromClock($this->clock),
metadata : new EventMetadata('actor')
);
$this->eventStore->append($ae);
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
final class DefaultEventRecorder implements EventRecorder
{
/** @var object[] */
private array $recordedEvents = [];
public function __construct(
private(set) int $version = 0,
) {
}
public function record(object $event): void
{
$this->recordedEvents[] = $event;
$this->version++;
}
/** @return object[] */
public function pullRecordedEvents(): array
{
$events = $this->recordedEvents;
$this->clearRecordedEvents();
return $events;
}
public function clearRecordedEvents(): void
{
$this->recordedEvents = [];
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Demo;
final class AggregateState
{
public bool $isDemo = false;
public function __construct()
{
}
public function whenDemoAggregateEvent(DemoAggregateEvent $event): void
{
$this->isDemo = true;
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Demo;
use App\Framework\EventSourcing\AggregateId;
use App\Framework\EventSourcing\AggregateRoot;
final readonly class DemoAggregate
{
public function __construct(
public AggregateRoot $root,
) {
}
public static function create(AggregateId $aggregateId, DemoAggregateDomainId $id): self
{
$root = new AggregateRoot(new AggregateState(), $aggregateId);
$root->eventRecorder->record(new DemoAggregateEvent($id));
return new self($root);
}
public static function rehydrate(AggregateId $id, iterable $events): self
{
$root = AggregateRoot::rehydrate($id, new AggregateState(), $events);
return new self($root);
}
public function eventHappened(DemoAggregateEvent $event): void
{
$this->root->applyAndRecord($event);
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Demo;
final readonly class DemoAggregateDomainId
{
public function __construct(
private string $id
) {
}
public function __toString(): string
{
return $this->id;
}
public function toString(): string
{
return $this->id;
}
public static function generate(): self
{
return new self(uniqid('demo_', true));
}
public static function fromString(string $id): self
{
return new self($id);
}
public function equals(self $other): bool
{
return $this->id === $other->id;
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Demo;
final readonly class DemoAggregateEvent
{
public function __construct(
public DemoAggregateDomainId $domainId,
public string $name = 'Demo Created',
public array $data = []
) {
}
public function getDomainId(): DemoAggregateDomainId
{
return $this->domainId;
}
public function getName(): string
{
return $this->name;
}
public function getData(): array
{
return $this->data;
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing\Demo;
use App\Framework\EventSourcing\AggregateId;
final class DemoTest
{
public function __construct()
{
$aggregateId = AggregateId::generate();
$domainId = DemoAggregateDomainId::generate();
$aggregate = DemoAggregate::create($aggregateId, $domainId);
$aggregate->eventHappened(new DemoAggregateEvent($domainId));
$aggregate->root->eventRecorder->pullRecordedEvents();
$rehydrated = DemoAggregate::rehydrate($aggregateId, $aggregate->root->eventRecorder->pullRecordedEvents());
var_dump("<pre>", $rehydrated);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
final readonly class EventMetadata
{
public function __construct(
public string $actor,
public ?string $commandId = null,
public array $context = []
) {
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
interface EventRecorder
{
public function record(object $event): void;
public function pullRecordedEvents(): array;
public function clearRecordedEvents(): void;
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\EventSourcing;
interface EventStore
{
public function append(AggregateEnvelope ...$envelopes): void;
public function loadStream(AggregateId $aggregateId): iterable; //yields AggregateEnvelope
}