diff --git a/phpstan.neon b/phpstan.neon index 64e5e97c..93f52b64 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 2 + level: 3 paths: - src/ - tests/ diff --git a/src/Application/CommandBus.php b/src/Application/CommandBus.php index 354c7488..cdc3eef8 100644 --- a/src/Application/CommandBus.php +++ b/src/Application/CommandBus.php @@ -14,7 +14,7 @@ namespace Streak\Application; use Streak\Domain; -use Streak\Domain\Exception\CommandNotSupported; +use Streak\Domain\Exception; /** * @author Alan Gabriel Bem @@ -22,7 +22,8 @@ interface CommandBus { /** - * @throws CommandNotSupported + * @throws Exception\CommandNotSupported + * @throws Exception\ConcurrentWriteDetected */ public function dispatch(Domain\Command $command): void; } diff --git a/src/Domain/AggregateRoot/EventSourcing.php b/src/Domain/AggregateRoot/EventSourcing.php index 139a985a..7171e8c6 100644 --- a/src/Domain/AggregateRoot/EventSourcing.php +++ b/src/Domain/AggregateRoot/EventSourcing.php @@ -101,7 +101,6 @@ final public function applyEvent(Event\Envelope $event): void } foreach ($this->eventSourcedEntities() as $entity) { - /** @var Event\Sourced\Entity $entity */ if ($entity->id()->equals($event->entityId())) { $aggregate = $entity; $stack = []; @@ -147,9 +146,9 @@ private function apply(Event $event): void } /** - * @return Event\Sourced\Entity[] + * @return \Generator */ - private function eventSourcedEntities(): iterable + private function eventSourcedEntities(): \Generator { yield from Event\Sourced\Entity\Helper::for($this)->extractEventSourcedEntities(); } diff --git a/src/Domain/Envelope.php b/src/Domain/Envelope.php index 382b79cb..5e6bec7a 100644 --- a/src/Domain/Envelope.php +++ b/src/Domain/Envelope.php @@ -16,6 +16,8 @@ use Streak\Domain\Id\UUID; /** + * @template T of object + * * @author Alan Gabriel Bem */ interface Envelope extends ValueObject @@ -24,12 +26,15 @@ public function uuid(): UUID; public function name(): string; + /** + * @return T + */ public function message(); /** * @param string $name * - * @return float|int|string|null + * @return null|scalar */ public function get($name); diff --git a/src/Domain/Event/Converter.php b/src/Domain/Event/Converter.php index 52ccba95..c9f9e751 100644 --- a/src/Domain/Event/Converter.php +++ b/src/Domain/Event/Converter.php @@ -20,11 +20,13 @@ interface Converter { /** * @throws Exception\ConversionToArrayNotPossible + * @throws \Exception */ public function objectToArray(object $object): array; /** * @throws Exception\ConversionToObjectNotPossible + * @throws \Exception */ public function arrayToObject(array $data): object; } diff --git a/src/Domain/Event/Envelope.php b/src/Domain/Event/Envelope.php index 47ac1484..cb6e1471 100644 --- a/src/Domain/Event/Envelope.php +++ b/src/Domain/Event/Envelope.php @@ -20,7 +20,8 @@ /** * @author Alan Gabriel Bem * - * @template T of Event + * @template TMessage as Event + * @implements Domain\Envelope * * @see \Streak\Domain\Event\EnvelopeTest */ @@ -33,26 +34,45 @@ final class Envelope implements Domain\Envelope public const METADATA_PRODUCER_ID = 'producer_id'; public const METADATA_ENTITY_TYPE = 'entity_type'; public const METADATA_ENTITY_ID = 'entity_id'; - private array $metadata = []; /** - * @param T $message + * @var-phpstan non-empty-array + * @var-psalm non-empty-array&array{ + * uuid: non-empty-string, + * name: non-empty-string, + * producer_type: class-string, + * producer_id: non-empty-string, + * entity_type: class-string, + * entity_id: non-empty-string, + * version?: positive-int, + * } + */ + private array $metadata; + + /** + * @param non-empty-string $name + * @param TMessage $message */ public function __construct(UUID $uuid, string $name, private Event $message, Domain\Id $producerId, Domain\Id $entityId, ?int $version = null) { - $this->metadata[self::METADATA_UUID] = $uuid->toString(); - $this->metadata[self::METADATA_NAME] = $name; - $this->metadata[self::METADATA_PRODUCER_TYPE] = $producerId::class; - $this->metadata[self::METADATA_PRODUCER_ID] = $producerId->toString(); - $this->metadata[self::METADATA_ENTITY_TYPE] = $entityId::class; - $this->metadata[self::METADATA_ENTITY_ID] = $entityId->toString(); + $this->metadata = [ + self::METADATA_UUID => $uuid->toString(), + self::METADATA_NAME => $name, + self::METADATA_PRODUCER_TYPE => $producerId::class, + self::METADATA_PRODUCER_ID => $producerId->toString(), + self::METADATA_ENTITY_TYPE => $entityId::class, + self::METADATA_ENTITY_ID => $entityId->toString(), + ]; if (null !== $version) { $this->metadata[self::METADATA_VERSION] = $version; } } /** - * @return Envelope + * @template TEvent of Event + * @param TEvent $message + * + * @return self */ public static function new(Event $message, Domain\Id $producerId, ?int $version = null): self { @@ -61,55 +81,54 @@ public static function new(Event $message, Domain\Id $producerId, ?int $version public function uuid(): UUID { - return new UUID($this->get(self::METADATA_UUID)); + return new UUID($this->metadata[self::METADATA_UUID]); } + /** + * @return non-empty-string + */ public function name(): string { - return $this->get(self::METADATA_NAME); + return $this->metadata[self::METADATA_NAME]; } - /** - * @return T - */ - public function message(): Event + public function message() { return $this->message; } public function producerId(): Domain\Id { - $class = $this->get(self::METADATA_PRODUCER_TYPE); + $class = $this->metadata[self::METADATA_PRODUCER_TYPE]; + $id = $this->metadata[self::METADATA_PRODUCER_ID]; /** @var class-string $class */ - /** @phpstan-var class-string $class */ - /** @psalm-var class-string $class */ - return $class::fromString($this->get(self::METADATA_PRODUCER_ID)); + return $class::fromString($id); } public function entityId(): Domain\Id { - $class = $this->get(self::METADATA_ENTITY_TYPE); + $class = $this->metadata[self::METADATA_ENTITY_TYPE]; + $id = $this->metadata[self::METADATA_ENTITY_ID]; /** @var class-string $class */ - /** @phpstan-var class-string $class */ - /** @psalm-var class-string $class */ - return $class::fromString($this->get(self::METADATA_ENTITY_ID)); + return $class::fromString($id); } public function version(): ?int { - return $this->get(self::METADATA_VERSION); + return $this->metadata[self::METADATA_VERSION] ?? null; } - public function set(string $name, $value): self + /** + * @param non-empty-string $name + * @return self + */ + public function set(string $name, bool|float|int|string $value): self { - if (empty($name)) { + if (empty($name)) { // @phpstan-ignore-line throw new \InvalidArgumentException('Name of the attribute can not be empty.'); } - if (!\is_scalar($value)) { - throw new \InvalidArgumentException(sprintf('Value for attribute "%s" is a scalar.', $name)); - } $new = new self( $this->uuid(), @@ -149,6 +168,9 @@ public function equals(object $envelope): bool return true; } + /** + * @return self + */ public function defineVersion(int $version): self { return new self( @@ -161,6 +183,9 @@ public function defineVersion(int $version): self ); } + /** + * @return self + */ public function defineEntityId(Domain\Id $entityId): self { return new self( diff --git a/src/Domain/Event/Exception/SourcingObjectWithEventFailed.php b/src/Domain/Event/Exception/SourcingObjectWithEventFailed.php index e5a948b0..a3df9899 100644 --- a/src/Domain/Event/Exception/SourcingObjectWithEventFailed.php +++ b/src/Domain/Event/Exception/SourcingObjectWithEventFailed.php @@ -29,7 +29,7 @@ public function __construct(private object $subject, private Event\Envelope $eve parent::__construct($message, 0, $previous); } - public function subject() + public function subject(): object { return $this->subject; } diff --git a/src/Domain/Event/Listener/Factory.php b/src/Domain/Event/Listener/Factory.php index 1ca6760d..6a42573d 100644 --- a/src/Domain/Event/Listener/Factory.php +++ b/src/Domain/Event/Listener/Factory.php @@ -15,20 +15,25 @@ use Streak\Domain\Event; use Streak\Domain\Event\Exception\InvalidEventGiven; -use Streak\Domain\Event\Listener; use Streak\Domain\Exception\InvalidIdGiven; /** + * @template T of Event\Listener + * * @author Alan Gabriel Bem */ interface Factory { /** + * @return T + * * @throws InvalidIdGiven */ - public function create(Listener\Id $id): Listener; + public function create(Event\Listener\Id $id): Event\Listener; /** + * @return T + * * @throws InvalidEventGiven */ public function createFor(Event\Envelope $event): Event\Listener; diff --git a/src/Domain/Event/Metadata.php b/src/Domain/Event/Metadata.php index b10c7b4d..6e272bbf 100644 --- a/src/Domain/Event/Metadata.php +++ b/src/Domain/Event/Metadata.php @@ -20,6 +20,9 @@ */ final class Metadata { + /** + * @var array + */ private array $metadata = []; private function __construct(array $metadata) @@ -34,7 +37,7 @@ public function set(string $name, string $value): void $this->metadata[$name] = $value; } - public function has(string $name) + public function has(string $name): bool { if (isset($this->metadata[$name])) { return true; @@ -52,7 +55,7 @@ public function get(string $name, string $default = null): ?string return $this->metadata[$name]; } - public static function fromObject($object): self + public static function fromObject(object $object): self { if (!isset($object->__streak_metadata)) { return new self([]); @@ -77,7 +80,7 @@ public static function fromArray(array $metadata): self return new self($metadata); } - public function toObject($object): void + public function toObject(object $object): void { $object->__streak_metadata = $this->metadata; } diff --git a/src/Domain/Event/Sourced/Entity/Helper.php b/src/Domain/Event/Sourced/Entity/Helper.php index 61e2efcf..76514cb8 100644 --- a/src/Domain/Event/Sourced/Entity/Helper.php +++ b/src/Domain/Event/Sourced/Entity/Helper.php @@ -42,9 +42,9 @@ public function applyEvent(Event\Envelope $event): void } /** - * @return Event\Sourced\Entity[] + * @return \Generator */ - public function extractEventSourcedEntities(): iterable + public function extractEventSourcedEntities(): \Generator { yield from self::doExtractEventSourcedEntities($this->object); } @@ -128,8 +128,10 @@ private static function applyEventByArgumentType(Event\Envelope $event, object & /** * Extract event sourced entities recursively. + * + * @return \Generator */ - private static function doExtractEventSourcedEntities(object $object, array &$ignored = []): iterable + private static function doExtractEventSourcedEntities(object $object, array &$ignored = []): \Generator { $ignored[] = $object; diff --git a/src/Domain/Event/Subscription/Factory.php b/src/Domain/Event/Subscription/Factory.php index b5ddf677..40d3a030 100644 --- a/src/Domain/Event/Subscription/Factory.php +++ b/src/Domain/Event/Subscription/Factory.php @@ -16,9 +16,14 @@ use Streak\Domain\Event; /** + * @template T of Event\Subscription + * * @author Alan Gabriel Bem */ interface Factory { + /** + * @return T + */ public function create(Event\Listener $listener): Event\Subscription; } diff --git a/src/Domain/Id.php b/src/Domain/Id.php index a68b7ec6..6ff97d06 100644 --- a/src/Domain/Id.php +++ b/src/Domain/Id.php @@ -20,7 +20,13 @@ */ interface Id extends Domain\ValueObject { + /** + * @return non-empty-string + */ public function toString(): string; - public static function fromString(string $id): Domain\Id; + /** + * @param non-empty-string $id + */ + public static function fromString(string $id): static; } diff --git a/src/Domain/Id/UUID.php b/src/Domain/Id/UUID.php index fbd16182..ac00e000 100644 --- a/src/Domain/Id/UUID.php +++ b/src/Domain/Id/UUID.php @@ -25,8 +25,12 @@ */ class UUID implements Domain\Id { + /** @var non-empty-string */ private string $value; + /** + * @param non-empty-string $value + */ final public function __construct(string $value) { $value = mb_strtolower($value); @@ -44,10 +48,10 @@ final public function __construct(string $value) throw new \InvalidArgumentException(); } - $this->value = $value; + $this->value = $value; // @phpstan-ignore-line } - public static function random() + public static function random(): static { $uuid = \Ramsey\Uuid\Uuid::uuid4()->toString(); @@ -72,10 +76,7 @@ public function toString(): string return $this->value; } - /** - * @return static - */ - public static function fromString(string $id): Domain\Id + public static function fromString(string $id): static { return new static($id); } diff --git a/src/Infrastructure/Application/Sensor/CommittingSensor/Factory.php b/src/Infrastructure/Application/Sensor/CommittingSensor/Factory.php index 78c114cc..38721aeb 100644 --- a/src/Infrastructure/Application/Sensor/CommittingSensor/Factory.php +++ b/src/Infrastructure/Application/Sensor/CommittingSensor/Factory.php @@ -28,7 +28,7 @@ public function __construct(private Sensor\Factory $factory, private UnitOfWork { } - public function create(): Sensor + public function create(): CommittingSensor { $sensor = $this->factory->create(); diff --git a/src/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorage.php b/src/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorage.php index af3316d1..9055aa3c 100644 --- a/src/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorage.php +++ b/src/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorage.php @@ -20,6 +20,8 @@ /** * @author Alan Gabriel Bem + * + * @see \Streak\Infrastructure\Domain\AggregateRoot\Snapshotter\Storage\RedisStorageTest */ final class RedisStorage implements Storage, Resettable { @@ -38,12 +40,17 @@ public function find(AggregateRoot $aggregate): string throw new SnapshotNotFound($aggregate); } + // Redis::get() can return Redis instance when it's in multi() mode. + if ($snapshot instanceof \Redis) { + throw new SnapshotNotFound($aggregate); + } + return $snapshot; } public function store(AggregateRoot $aggregate, string $snapshot): void { - $this->redis->set($this->key($aggregate), (string) $snapshot); + $this->redis->set($this->key($aggregate), $snapshot); } public function reset(): bool diff --git a/src/Infrastructure/Domain/Event/Converter/CompositeConverter.php b/src/Infrastructure/Domain/Event/Converter/CompositeConverter.php index bbf8f50a..9555fcbc 100644 --- a/src/Infrastructure/Domain/Event/Converter/CompositeConverter.php +++ b/src/Infrastructure/Domain/Event/Converter/CompositeConverter.php @@ -38,9 +38,6 @@ public function addConverter(Converter $converter): void $this->converters[] = $converter; } - /** - * @throws Exception\ConversionToArrayNotPossible - */ public function objectToArray(object $object): array { foreach ($this->converters as $converter) { @@ -56,9 +53,6 @@ public function objectToArray(object $object): array throw new Exception\ConversionToArrayNotPossible($object); } - /** - * @throws Exception\ConversionToObjectNotPossible - */ public function arrayToObject(array $data): object { $previous = null; diff --git a/src/Infrastructure/Domain/Event/LoggingListener/Factory.php b/src/Infrastructure/Domain/Event/LoggingListener/Factory.php index 761a368d..798e628c 100644 --- a/src/Infrastructure/Domain/Event/LoggingListener/Factory.php +++ b/src/Infrastructure/Domain/Event/LoggingListener/Factory.php @@ -18,6 +18,8 @@ use Streak\Infrastructure\Domain\Event\LoggingListener; /** + * @implements Event\Listener\Factory + * * @author Alan Gabriel Bem * * @see \Streak\Infrastructure\Domain\Event\LoggingListener\FactoryTest @@ -28,14 +30,14 @@ public function __construct(private Event\Listener\Factory $factory, private Log { } - public function create(Event\Listener\Id $id): Event\Listener + public function create(Event\Listener\Id $id): LoggingListener { $saga = $this->factory->create($id); return new LoggingListener($saga, $this->logger); } - public function createFor(Event\Envelope $event): Event\Listener + public function createFor(Event\Envelope $event): LoggingListener { $listener = $this->factory->createFor($event); diff --git a/src/Infrastructure/Domain/Event/NullListener.php b/src/Infrastructure/Domain/Event/NullListener.php index 26fa4569..ebd12a25 100644 --- a/src/Infrastructure/Domain/Event/NullListener.php +++ b/src/Infrastructure/Domain/Event/NullListener.php @@ -30,7 +30,7 @@ public function on(Event\Envelope $event): bool return true; } - public static function from(Event\Listener $listener) + public static function from(Event\Listener $listener): self { return new self($listener->id()); } diff --git a/src/Infrastructure/Domain/Event/Sourced/Subscription/InMemoryState.php b/src/Infrastructure/Domain/Event/Sourced/Subscription/InMemoryState.php index 3364262a..72b6d265 100644 --- a/src/Infrastructure/Domain/Event/Sourced/Subscription/InMemoryState.php +++ b/src/Infrastructure/Domain/Event/Sourced/Subscription/InMemoryState.php @@ -22,6 +22,9 @@ */ class InMemoryState implements State { + /** + * @var array + */ private array $state = []; private function __construct() @@ -55,7 +58,10 @@ public function get(string $name) return $this->state[$name]; } - public function set(string $name, $value): State + /** + * @param array|scalar|null $value + */ + public function set(string $name, $value): self { $this->validate($name, $value); @@ -94,6 +100,9 @@ public static function empty(): self return new self(); } + /** + * @param array|scalar|null $value + */ private function validate(string $name, $value): void { if (true === empty($name)) { diff --git a/src/Infrastructure/Domain/Event/Subscription/CommittingSubscription/Factory.php b/src/Infrastructure/Domain/Event/Subscription/CommittingSubscription/Factory.php index b46bfc3f..3eb9460c 100644 --- a/src/Infrastructure/Domain/Event/Subscription/CommittingSubscription/Factory.php +++ b/src/Infrastructure/Domain/Event/Subscription/CommittingSubscription/Factory.php @@ -19,6 +19,8 @@ use Streak\Infrastructure\Domain\UnitOfWork; /** + * @implements Event\Subscription\Factory + * * @author Alan Gabriel Bem * * @see \Streak\Infrastructure\Domain\Event\Subscription\CommittingSubscription\FactoryTest @@ -29,7 +31,7 @@ public function __construct(private Subscription\Factory $factory, private UnitO { } - public function create(Event\Listener $listener): Event\Subscription + public function create(Event\Listener $listener): CommittingSubscription { $subscription = $this->factory->create($listener); diff --git a/src/Infrastructure/Domain/Event/Subscription/DAO.php b/src/Infrastructure/Domain/Event/Subscription/DAO.php index 8087dcc0..465ae458 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DAO.php +++ b/src/Infrastructure/Domain/Event/Subscription/DAO.php @@ -30,7 +30,7 @@ public function exists(Listener\Id $id): bool; /** * @param string[] $types * - * @return DAO\Subscription[] + * @return iterable */ public function all(array $types = [], ?bool $completed = null): iterable; } diff --git a/src/Infrastructure/Domain/Event/Subscription/DAO/DbalPostgresDAO.php b/src/Infrastructure/Domain/Event/Subscription/DAO/DbalPostgresDAO.php index 409ebb42..47874063 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DAO/DbalPostgresDAO.php +++ b/src/Infrastructure/Domain/Event/Subscription/DAO/DbalPostgresDAO.php @@ -30,8 +30,13 @@ */ class DbalPostgresDAO implements DAO { - public function __construct(private Subscription\Factory $subscriptions, private Event\Listener\Factory $listeners, private Connection $connection, private Event\Converter $converter) - { + public function __construct( + /** @var Subscription\Factory */ + private Subscription\Factory $subscriptions, + private Event\Listener\Factory $listeners, + private Connection $connection, + private Event\Converter $converter + ) { } /** @@ -69,11 +74,9 @@ public function exists(Listener\Id $id): bool } /** - * @param string[] $types + * @return \Generator * * @throws \Doctrine\DBAL\DBALException - * - * @return Subscription[] */ public function all(array $types = [], ?bool $completed = null): iterable { @@ -186,7 +189,7 @@ public function drop(): void $statement->execute(); } - private function fromRow($row): Subscription + private function fromRow($row): DAO\Subscription { $id = $row['subscription_type']; $id = $id::fromString($row['subscription_id']); @@ -306,11 +309,9 @@ private function doSave(DAO\Subscription $subscription): void } /** - * * @throws \Doctrine\DBAL\DBALException - * @return \Streak\Infrastructure\Domain\Event\Subscription\DAO\Subscription|null */ - private function doOne(Event\Listener\Id $id) + private function doOne(Event\Listener\Id $id): ?DAO\Subscription { $sql = 'SELECT subscription_type, subscription_id, subscription_version, state, started_by, started_at, last_processed_event, last_event_processed_at, completed, paused_at FROM subscriptions WHERE subscription_type = :subscription_type AND subscription_id = :subscription_id LIMIT 1'; @@ -354,6 +355,8 @@ private function doExists(Listener\Id $id): bool } /** + * @return \Generator + * * @throws \Doctrine\DBAL\DBALException */ private function doAll(array $types, ?bool $completed): \Generator diff --git a/src/Infrastructure/Domain/Event/Subscription/DAO/InMemoryDAO.php b/src/Infrastructure/Domain/Event/Subscription/DAO/InMemoryDAO.php index 56360950..cac5f52f 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DAO/InMemoryDAO.php +++ b/src/Infrastructure/Domain/Event/Subscription/DAO/InMemoryDAO.php @@ -60,7 +60,7 @@ public function exists(Listener\Id $id): bool public function all(array $types = [], ?bool $completed = null): iterable { - foreach ($this->subscriptions as $key => $stored) { + foreach ($this->subscriptions as $stored) { if (\count($types)) { $type = $stored->id()::class; if (false === \in_array($type, $types)) { diff --git a/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription.php b/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription.php index 3c09522f..29b2e418 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription.php +++ b/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription.php @@ -16,6 +16,7 @@ use Streak\Domain\Clock; use Streak\Domain\Event; use Streak\Domain\Event\Listener; +use Streak\Domain\Event\Listener\State; use Streak\Domain\Event\Subscription\Exception; use Streak\Domain\EventStore; use Streak\Infrastructure\Domain\Event\Sourced\Subscription\InMemoryState; @@ -28,12 +29,12 @@ class Subscription implements Event\Subscription { private const LIMIT_TO_INITIAL_STREAM = 0; - private InMemoryState $state; + private State $state; private ?Event\Envelope $startedBy = null; - private ?\DateTimeImmutable $startedAt = null; + private ?\DateTimeImmutable $startedAt = null; // @TODO: check if needed private ?\DateTimeImmutable $pausedAt = null; private ?Event\Envelope $lastProcessedEvent = null; - private ?\DateTimeImmutable $lastEventProcessedAt = null; + private ?\DateTimeImmutable $lastEventProcessedAt = null; // @TODO: check if needed private int $version = 0; private bool $completed = false; diff --git a/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription/Factory.php b/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription/Factory.php index 3568288c..ccfc119e 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription/Factory.php +++ b/src/Infrastructure/Domain/Event/Subscription/DAO/Subscription/Factory.php @@ -15,9 +15,11 @@ use Streak\Domain\Clock; use Streak\Domain\Event; -use Streak\Infrastructure\Domain\Event\Subscription\DAO\Subscription; +use Streak\Infrastructure\Domain\Event\Subscription\DAO; /** + * @implements Event\Subscription\Factory + * * @author Alan Gabriel Bem * * @see \Streak\Infrastructure\Domain\Event\Subscription\DAO\Subscription\FactoryTest @@ -28,8 +30,8 @@ public function __construct(private Clock $clock) { } - public function create(Event\Listener $listener): Event\Subscription + public function create(Event\Listener $listener): DAO\Subscription { - return new Subscription($listener, $this->clock); + return new DAO\Subscription($listener, $this->clock); } } diff --git a/src/Infrastructure/Domain/Event/Subscription/DbalTransactionalSubscription/Factory.php b/src/Infrastructure/Domain/Event/Subscription/DbalTransactionalSubscription/Factory.php index 86c89620..0fc0e64c 100644 --- a/src/Infrastructure/Domain/Event/Subscription/DbalTransactionalSubscription/Factory.php +++ b/src/Infrastructure/Domain/Event/Subscription/DbalTransactionalSubscription/Factory.php @@ -18,6 +18,8 @@ use Streak\Infrastructure\Domain\Event\Subscription\DbalTransactionalSubscription; /** + * @implements Event\Subscription\Factory + * * @author Alan Gabriel Bem * * @see \Streak\Infrastructure\Domain\Event\Subscription\DbalTransactionalSubscription\FactoryTest @@ -35,7 +37,7 @@ public function __construct(private Event\Subscription\Factory $factory, private $this->maxTransactionSize = $maxTransactionSize; } - public function create(Event\Listener $listener): Event\Subscription + public function create(Event\Listener $listener): DbalTransactionalSubscription { $subscription = $this->factory->create($listener); diff --git a/src/Infrastructure/Domain/EventBus/InMemoryEventBus.php b/src/Infrastructure/Domain/EventBus/InMemoryEventBus.php index 1e12e4cc..01277abf 100644 --- a/src/Infrastructure/Domain/EventBus/InMemoryEventBus.php +++ b/src/Infrastructure/Domain/EventBus/InMemoryEventBus.php @@ -48,7 +48,7 @@ public function add(Event\Listener $listener): void } } - $this->listeners[] = $listener; + $this->listeners->enqueue($listener); } public function remove(Event\Listener $listener): void diff --git a/src/Infrastructure/Domain/EventStore/DbalPostgresEventStore.php b/src/Infrastructure/Domain/EventStore/DbalPostgresEventStore.php index 43ec1b1d..0bb95097 100644 --- a/src/Infrastructure/Domain/EventStore/DbalPostgresEventStore.php +++ b/src/Infrastructure/Domain/EventStore/DbalPostgresEventStore.php @@ -691,7 +691,7 @@ private function toRow(Event\Envelope $event): array ]; } - private function extractIdForConcurrentWrite(UniqueConstraintViolationException $e) + private function extractIdForConcurrentWrite(UniqueConstraintViolationException $e): Domain\Id|null { // example: // SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "events_producer_type_producer_id_producer_version_key" @@ -717,7 +717,7 @@ private function extractIdForConcurrentWrite(UniqueConstraintViolationException return $this->toId($matches['type'], $matches['id']); } - private function extractIdForEventAlreadyInStore(UniqueConstraintViolationException $e) + private function extractIdForEventAlreadyInStore(UniqueConstraintViolationException $e): UUID|null { // example: // SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "events_uuid_key" diff --git a/src/Infrastructure/Domain/Serializer.php b/src/Infrastructure/Domain/Serializer.php index a477381c..5fb5bccb 100644 --- a/src/Infrastructure/Domain/Serializer.php +++ b/src/Infrastructure/Domain/Serializer.php @@ -20,5 +20,5 @@ interface Serializer { public function serialize($subject): string; - public function unserialize($serialized); + public function unserialize(string $serialized); } diff --git a/src/Infrastructure/Domain/Serializer/IGBinarySerializer.php b/src/Infrastructure/Domain/Serializer/IGBinarySerializer.php index 5bccb1b5..59f824ae 100644 --- a/src/Infrastructure/Domain/Serializer/IGBinarySerializer.php +++ b/src/Infrastructure/Domain/Serializer/IGBinarySerializer.php @@ -27,7 +27,7 @@ public function serialize($subject): string return igbinary_serialize($subject); } - public function unserialize($serialized) + public function unserialize(string $serialized) { return igbinary_unserialize($serialized); } diff --git a/src/Infrastructure/Domain/Serializer/PhpSerializer.php b/src/Infrastructure/Domain/Serializer/PhpSerializer.php index f2873f25..d15f75b3 100644 --- a/src/Infrastructure/Domain/Serializer/PhpSerializer.php +++ b/src/Infrastructure/Domain/Serializer/PhpSerializer.php @@ -27,7 +27,7 @@ public function serialize($subject): string return serialize($subject); } - public function unserialize($serialized) + public function unserialize(string $serialized) { return unserialize($serialized); } diff --git a/src/Infrastructure/Domain/Testing/AggregateRoot/TestCase.php b/src/Infrastructure/Domain/Testing/AggregateRoot/TestCase.php index 7b187577..71e776cf 100644 --- a/src/Infrastructure/Domain/Testing/AggregateRoot/TestCase.php +++ b/src/Infrastructure/Domain/Testing/AggregateRoot/TestCase.php @@ -59,7 +59,7 @@ protected function createSnapshotterSerializer(): Serializer return new PhpSerializer(); } - protected function createSnapshotterStorage() + protected function createSnapshotterStorage(): Snapshotter\Storage\InMemoryStorage { return new Snapshotter\Storage\InMemoryStorage(); } diff --git a/src/Infrastructure/Domain/Testing/Listener/TestCase.php b/src/Infrastructure/Domain/Testing/Listener/TestCase.php index 903298ac..a227596e 100644 --- a/src/Infrastructure/Domain/Testing/Listener/TestCase.php +++ b/src/Infrastructure/Domain/Testing/Listener/TestCase.php @@ -38,7 +38,7 @@ public function given(Domain\Event ...$events): Scenario\When abstract public function createFactory(Application\CommandBus $bus): Domain\Event\Listener\Factory; - private function createScenario(Application\CommandBus $bus, Domain\Event\Listener\Factory $factory): Listener\Scenario + private function createScenario(SynchronousCommandBus $bus, Domain\Event\Listener\Factory $factory): Listener\Scenario { return new Listener\Scenario($bus, $factory); } diff --git a/src/Infrastructure/Domain/UnitOfWork/SubscriptionDAOUnitOfWork.php b/src/Infrastructure/Domain/UnitOfWork/SubscriptionDAOUnitOfWork.php index 186cfd3a..69f2b0ae 100644 --- a/src/Infrastructure/Domain/UnitOfWork/SubscriptionDAOUnitOfWork.php +++ b/src/Infrastructure/Domain/UnitOfWork/SubscriptionDAOUnitOfWork.php @@ -27,7 +27,7 @@ class SubscriptionDAOUnitOfWork implements UnitOfWork { /** - * @var Subscription[] + * @var DAO\Subscription[] */ private array $uncommited = []; @@ -98,7 +98,7 @@ public function commit(): \Generator try { while ($object = array_shift($this->uncommited)) { - /** @var Subscription $object */ + /** @var DAO\Subscription $object */ try { $this->dao->save($object); @@ -125,16 +125,12 @@ public function clear(): void private function supports(object $subscription): bool { - if ($subscription instanceof DAO\Subscription) { - return true; - } - while ($subscription instanceof Subscription\Decorator) { $subscription = $subscription->subscription(); + } - if ($subscription instanceof DAO\Subscription) { - return true; - } + if ($subscription instanceof DAO\Subscription) { + return true; } return false; diff --git a/tests/Domain/Event/EnvelopeTest.php b/tests/Domain/Event/EnvelopeTest.php index d0624e6f..2a938761 100644 --- a/tests/Domain/Event/EnvelopeTest.php +++ b/tests/Domain/Event/EnvelopeTest.php @@ -127,29 +127,6 @@ public function testSettingEmptyAttributeName(): void $envelope->set('', 'value-1'); } - - public function nonScalarValues() - { - return [ - [[]], - [new \stdClass()], - ]; - } - - /** - * @dataProvider nonScalarValues - * - * @param mixed $value - */ - public function testSettingNonScalarAttribute($value): void - { - $envelope = Event\Envelope::new($this->event1, UUID::random(), \PHP_INT_MAX); - - $exception = new \InvalidArgumentException('Value for attribute "attr-1" is a scalar.'); - $this->expectExceptionObject($exception); - - $envelope->set('attr-1', $value); - } } namespace Streak\Domain\Event\EnvelopeTest; diff --git a/tests/Domain/EventSourcingTest.php b/tests/Domain/EventSourcingTest.php index 85ac0bd0..bbe4da6e 100644 --- a/tests/Domain/EventSourcingTest.php +++ b/tests/Domain/EventSourcingTest.php @@ -637,7 +637,7 @@ class EventSourcedAggregateStub implements Event\Sourced\Aggregate use Aggregate\Identification; /** - * @var Event\Envelope[] + * @var array */ private array $appliedEvents = []; @@ -671,12 +671,12 @@ public function appliedEvents(): array private function applyEvent1(Event1 $event): void { - $this->appliedEvents = [$event]; + $this->appliedEvents[] = $event; } private function applyEvent2(Event2 $event): void { - $this->appliedEvents = [$event]; + $this->appliedEvents[] = $event; } } diff --git a/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/PostgresStorageTest.php b/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/PostgresStorageTest.php index 28923a09..055f1311 100644 --- a/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/PostgresStorageTest.php +++ b/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/PostgresStorageTest.php @@ -15,7 +15,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; -use Streak\Domain; use Streak\Domain\AggregateRoot; use Streak\Infrastructure\Domain\AggregateRoot\Snapshotter\Storage\Exception\SnapshotNotFound; @@ -169,10 +168,12 @@ private function createAggregateRootStub(string $id): AggregateRoot } } -class IdStub implements AggregateRoot\Id +final class IdStub implements AggregateRoot\Id { - public function __construct(private string $id) - { + public function __construct( + /** @var non-empty-string $id */ + private string $id + ) { } public function equals(object $object): bool @@ -185,7 +186,7 @@ public function toString(): string return $this->id; } - public static function fromString(string $id): Domain\Id + public static function fromString(string $id): static { return new self($id); } diff --git a/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisTest.php b/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorageTest.php similarity index 74% rename from tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisTest.php rename to tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorageTest.php index 3a52df2d..f66a8837 100644 --- a/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisTest.php +++ b/tests/Infrastructure/Domain/AggregateRoot/Snapshotter/Storage/RedisStorageTest.php @@ -23,7 +23,7 @@ * * @covers \Streak\Infrastructure\Domain\AggregateRoot\Snapshotter\Storage\RedisStorage */ -class RedisTest extends Storage\TestCase +class RedisStorageTest extends Storage\TestCase { public function testResetting(): void { @@ -40,12 +40,27 @@ public function testResetting(): void $this->storage->find($this->aggregate1); } + public function testRedisInMultiMode(): void + { + $redis = $this->newRedis(); + $storage = new RedisStorage($redis->multi()); + + $this->expectExceptionObject(new SnapshotNotFound($this->aggregate1)); + + $storage->find($this->aggregate1); + } + protected function newStorage(): Storage + { + return new RedisStorage($this->newRedis()); + } + + private function newRedis(): \Redis { $redis = new \Redis(); $redis->connect($_ENV['PHPUNIT_REDIS_HOSTNAME'], (int) $_ENV['PHPUNIT_REDIS_PORT']); $redis->select((int) $_ENV['PHPUNIT_REDIS_DATABASE']); - return new RedisStorage($redis); + return $redis; } } diff --git a/tests/Infrastructure/Domain/Event/Subscription/EventSourcedRepositoryTest.php b/tests/Infrastructure/Domain/Event/Subscription/EventSourcedRepositoryTest.php index 98bc5ef7..ad5cda86 100644 --- a/tests/Infrastructure/Domain/Event/Subscription/EventSourcedRepositoryTest.php +++ b/tests/Infrastructure/Domain/Event/Subscription/EventSourcedRepositoryTest.php @@ -243,7 +243,9 @@ public function testFindingSubscription(): void ->with(self::callback(function (Event\Stream $stream) use ($event1, $event2, $event3) { $stream = iterator_to_array($stream); - return self::equalTo([$event1, $event2, $event3])->evaluate($stream); + self::equalTo([$event1, $event2, $event3])->evaluate($stream); + + return true; })) ; @@ -300,7 +302,9 @@ public function testFindingPreviouslyRestartedSubscription(): void $stream = iterator_to_array($stream); // streaming from SubscriptionRestarted event - return self::equalTo([$event1, $event2, $event3, $event4, $event5, $event6])->evaluate($stream); + self::equalTo([$event1, $event2, $event3, $event4, $event5, $event6])->evaluate($stream); + + return true; })) ; diff --git a/tests/Infrastructure/Domain/EventStore/EventStoreTestCase.php b/tests/Infrastructure/Domain/EventStore/EventStoreTestCase.php index 0b6de8c2..a4e1ecb0 100644 --- a/tests/Infrastructure/Domain/EventStore/EventStoreTestCase.php +++ b/tests/Infrastructure/Domain/EventStore/EventStoreTestCase.php @@ -366,8 +366,10 @@ abstract protected function newEventStore(): EventStore; abstract class ValueId implements Domain\Id { - final public function __construct(private string $value) - { + final public function __construct( + /** @var non-empty-string $value */ + private string $value + ) { } public function equals(object $id): bool @@ -384,7 +386,7 @@ public function toString(): string return $this->value; } - public static function fromString(string $id): Domain\Id + public static function fromString(string $id): static { return new static($id); }