Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Header as Interface #545

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ deptrac:
collectors:
- type: directory
value: src/Metadata/Event/.*
- name: MetadataMessage
collectors:
- type: directory
value: src/Metadata/Message/.*
- name: MetadataSubscriber
collectors:
- type: directory
Expand All @@ -60,8 +56,6 @@ deptrac:
value: MetadataAggregate
- type: layer
value: MetadataEvent
- type: layer
value: MetadataMessage
- type: layer
value: MetadataSubscriber
- name: Pipeline
Expand Down Expand Up @@ -96,6 +90,7 @@ deptrac:
ruleset:
Aggregate:
- Attribute
- Message
- MetadataAggregate
Attribute:
Clock:
Expand All @@ -117,8 +112,9 @@ deptrac:
- Attribute
- Message
Message:
- MetadataMessage
- Serializer
- Aggregate
- Debug
- Store
Metadata:
MetadataAggregate:
- Aggregate
Expand All @@ -127,9 +123,6 @@ deptrac:
MetadataEvent:
- Attribute
- Metadata
MetadataMessage:
- Attribute
- Metadata
MetadataSubscriber:
- Attribute
- Metadata
Expand Down
2 changes: 0 additions & 2 deletions docs/pages/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,10 @@ $projectionConnection = DriverManager::getConnection([
$mailer = /* your own mailer */;

$serializer = DefaultEventSerializer::createFromPaths(['src/Domain/Hotel/Event']);
$aggregateRegistry = (new AttributeAggregateRootRegistryFactory)->create(['src/Domain/Hotel']);

$eventStore = new DoctrineDbalStore(
$connection,
$serializer,
$aggregateRegistry,
);

$hotelProjector = new HotelProjector($projectionConnection);
Expand Down
37 changes: 31 additions & 6 deletions src/Aggregate/AggregateHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,45 @@
namespace Patchlevel\EventSourcing\Aggregate;

use DateTimeImmutable;
use Patchlevel\EventSourcing\Attribute\Header;
use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;
use Patchlevel\EventSourcing\Message\Header;

/** @psalm-immutable */
#[Header('aggregate')]
final class AggregateHeader
/**
* @template-implements Header<array{aggregateName: string, aggregateId: string, playhead: int, recordedOn: string}>
* @psalm-immutable
*/
final class AggregateHeader implements Header
{
/** @param positive-int $playhead */
public function __construct(
public readonly string $aggregateName,
public readonly string $aggregateId,
public readonly int $playhead,
#[DateTimeImmutableNormalizer]
public readonly DateTimeImmutable $recordedOn,
) {
}

public static function name(): string
{
return 'aggregate';
}

public static function fromJsonSerialize(array $data): static

Check failure on line 30 in src/Aggregate/AggregateHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

ImplementedParamTypeMismatch

src/Aggregate/AggregateHeader.php:30:52: ImplementedParamTypeMismatch: Argument 1 of Patchlevel\EventSourcing\Aggregate\AggregateHeader::fromJsonSerialize has wrong type 'array<array-key, mixed>', expecting 'Patchlevel\EventSourcing\Message\T' as defined by Patchlevel\EventSourcing\Message\Header::fromJsonSerialize (see https://psalm.dev/199)

Check failure on line 30 in src/Aggregate/AggregateHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedInferredReturnType

src/Aggregate/AggregateHeader.php:30:60: MixedInferredReturnType: Could not verify return type 'Patchlevel\EventSourcing\Aggregate\AggregateHeader' for Patchlevel\EventSourcing\Aggregate\AggregateHeader::fromJsonSerialize (see https://psalm.dev/047)
{
return new self(
$data['aggregateName'],
$data['aggregateId'],
$data['playhead'],

Check failure on line 35 in src/Aggregate/AggregateHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Parameter #3 $playhead of class Patchlevel\EventSourcing\Aggregate\AggregateHeader constructor expects int<1, max>, int given.
new DateTimeImmutable($data['recordedOn']),
);
}

public function jsonSerialize(): array
{
return [
'aggregateName' => $this->aggregateName,
'aggregateId' => $this->aggregateId,
'playhead' => $this->playhead,
'recordedOn' => $this->recordedOn->format(DateTimeImmutable::ATOM),
];
}
}
16 changes: 0 additions & 16 deletions src/Attribute/Header.php

This file was deleted.

4 changes: 1 addition & 3 deletions src/Console/Command/ShowAggregateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Patchlevel\EventSourcing\Console\InputHelper;
use Patchlevel\EventSourcing\Console\OutputStyle;
use Patchlevel\EventSourcing\Message\Serializer\HeadersSerializer;
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Patchlevel\EventSourcing\Store\Criteria;
Expand All @@ -31,7 +30,6 @@ final class ShowAggregateCommand extends Command
public function __construct(
private readonly Store $store,
private readonly EventSerializer $eventSerializer,
private readonly HeadersSerializer $headersSerializer,
private readonly AggregateRootRegistry $aggregateRootRegistry,
) {
parent::__construct();
Expand Down Expand Up @@ -78,7 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$hasMessage = false;
foreach ($stream as $message) {
$hasMessage = true;
$console->message($this->eventSerializer, $this->headersSerializer, $message);
$console->message($this->eventSerializer, $message);
}

$stream->close();
Expand Down
4 changes: 1 addition & 3 deletions src/Console/Command/ShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Patchlevel\EventSourcing\Console\InputHelper;
use Patchlevel\EventSourcing\Console\OutputStyle;
use Patchlevel\EventSourcing\Message\Serializer\HeadersSerializer;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Patchlevel\EventSourcing\Store\Store;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -26,7 +25,6 @@ final class ShowCommand extends Command
public function __construct(
private readonly Store $store,
private readonly EventSerializer $eventSerializer,
private readonly HeadersSerializer $headersSerializer,
) {
parent::__construct();
}
Expand Down Expand Up @@ -74,7 +72,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
break 2;
}

$console->message($this->eventSerializer, $this->headersSerializer, $message);
$console->message($this->eventSerializer, $message);

$stream->next();

Expand Down
4 changes: 1 addition & 3 deletions src/Console/Command/WatchCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Patchlevel\EventSourcing\Console\InputHelper;
use Patchlevel\EventSourcing\Console\OutputStyle;
use Patchlevel\EventSourcing\Message\Serializer\HeadersSerializer;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Patchlevel\EventSourcing\Store\Criteria;
use Patchlevel\EventSourcing\Store\Store;
Expand All @@ -26,7 +25,6 @@ final class WatchCommand extends Command
public function __construct(
private readonly Store $store,
private readonly EventSerializer $eventSerializer,
private readonly HeadersSerializer $headersSerializer,
) {
parent::__construct();
}
Expand Down Expand Up @@ -76,7 +74,7 @@ function () use ($console, &$index, $aggregate, $aggregateId): void {
);

foreach ($stream as $message) {
$console->message($this->eventSerializer, $this->headersSerializer, $message);
$console->message($this->eventSerializer, $message);
$index = $stream->index();
}

Expand Down
23 changes: 6 additions & 17 deletions src/Console/OutputStyle.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@
namespace Patchlevel\EventSourcing\Console;

use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Message\Serializer\HeadersSerializer;
use Patchlevel\EventSourcing\Serializer\Encoder\Encoder;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;

use function array_keys;
use function array_values;
use function json_encode;
use function sprintf;

use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;

final class OutputStyle extends SymfonyStyle
{
public function message(
EventSerializer $eventSerializer,
HeadersSerializer $headersSerializer,
Message $message,
): void {
$event = $message->event();
Expand All @@ -42,25 +44,12 @@
return;
}

try {
$headers = $headersSerializer->serialize($message->headers(), [Encoder::OPTION_PRETTY_PRINT => true]);
} catch (Throwable $error) {
$this->error(
sprintf(
'Error while serializing headers: %s',
$error->getMessage(),
),
);

if ($this->isVeryVerbose()) {
$this->throwable($error);
}

return;
foreach ($message->headers() as $header) {
$headers[$header::name()] = json_encode($header, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);

Check failure on line 48 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

PossiblyUndefinedVariable

src/Console/OutputStyle.php:48:13: PossiblyUndefinedVariable: Possibly undefined variable $headers, first seen on line 48 (see https://psalm.dev/018)

Check failure on line 48 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Console/OutputStyle.php:48:13: MixedArrayAssignment: Cannot access array value on mixed variable $headers[string] (see https://psalm.dev/117)
}

$this->title($data->name);
$this->horizontalTable(array_keys($headers), [array_values($headers)]);

Check failure on line 52 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

PossiblyUndefinedVariable

src/Console/OutputStyle.php:52:43: PossiblyUndefinedVariable: Possibly undefined variable $headers, first seen on line 48 (see https://psalm.dev/018)

Check failure on line 52 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArgument

src/Console/OutputStyle.php:52:43: MixedArgument: Argument 1 of array_keys cannot be mixed, expecting array<array-key, mixed> (see https://psalm.dev/030)

Check failure on line 52 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Variable $headers might not be defined.

Check failure on line 52 in src/Console/OutputStyle.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Variable $headers might not be defined.
$this->block($data->payload);
}

Expand Down
28 changes: 24 additions & 4 deletions src/Debug/Trace/TraceHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,35 @@

namespace Patchlevel\EventSourcing\Debug\Trace;

use Patchlevel\EventSourcing\Attribute\Header;
use Patchlevel\EventSourcing\Message\Header;

/** @experimental */
#[Header('trace')]
final class TraceHeader
/**
* @experimental
* @template-implements Header<array{traces: list<array{name: string, category: string}>}>
* @psalm-immutable
*/
final class TraceHeader implements Header
{
/** @param list<array{name: string, category: string}> $traces */
public function __construct(
public readonly array $traces,
) {
}

public static function name(): string
{
return 'trace';
}

public static function fromJsonSerialize(array $data): static

Check failure on line 27 in src/Debug/Trace/TraceHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

ImplementedParamTypeMismatch

src/Debug/Trace/TraceHeader.php:27:52: ImplementedParamTypeMismatch: Argument 1 of Patchlevel\EventSourcing\Debug\Trace\TraceHeader::fromJsonSerialize has wrong type 'array<array-key, mixed>', expecting 'Patchlevel\EventSourcing\Message\T' as defined by Patchlevel\EventSourcing\Message\Header::fromJsonSerialize (see https://psalm.dev/199)
{
return new self($data['traces']);

Check failure on line 29 in src/Debug/Trace/TraceHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

UndefinedDocblockClass

src/Debug/Trace/TraceHeader.php:29:25: UndefinedDocblockClass: Docblock-defined class, interface or enum named Patchlevel\EventSourcing\Message\T does not exist (see https://psalm.dev/200)

Check failure on line 29 in src/Debug/Trace/TraceHeader.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArgument

src/Debug/Trace/TraceHeader.php:29:25: MixedArgument: Argument 1 of Patchlevel\EventSourcing\Debug\Trace\TraceHeader::__construct cannot be mixed, expecting list<array{category: string, name: string}> (see https://psalm.dev/030)
}

public function jsonSerialize(): array
{
return [
'traces' => $this->traces,
];
}
}
22 changes: 22 additions & 0 deletions src/Message/Header.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Message;

use JsonSerializable;

/**
* @psalm-immutable
* @template T of array
*/
interface Header extends JsonSerializable
{
public static function name(): string;

/** @param T $data */

Check failure on line 17 in src/Message/Header.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

UndefinedDocblockClass

src/Message/Header.php:17:16: UndefinedDocblockClass: Docblock-defined class, interface or enum named Patchlevel\EventSourcing\Message\T does not exist (see https://psalm.dev/200)
public static function fromJsonSerialize(array $data): static;

/** @return T */
public function jsonSerialize(): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Metadata\Message;
namespace Patchlevel\EventSourcing\Message;

use Patchlevel\EventSourcing\Metadata\MetadataException;
use RuntimeException;

use function sprintf;

final class HeaderClassNotRegistered extends MetadataException
final class HeaderClassNotRegistered extends RuntimeException
{
public function __construct(string $headerClass)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Metadata\Message;
namespace Patchlevel\EventSourcing\Message;

use Patchlevel\EventSourcing\Metadata\MetadataException;
use RuntimeException;

use function sprintf;

final class HeaderNameNotRegistered extends MetadataException
final class HeaderNameNotRegistered extends RuntimeException
{
public function __construct(string $name)
{
Expand Down
10 changes: 5 additions & 5 deletions src/Message/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
final class Message
{
/** @var array<class-string, object> */
/** @var array<class-string, Header> */
private array $headers = [];

/** @param T $event */
Expand All @@ -36,7 +36,7 @@
return new self($event);
}

/** @param iterable<object> $headers */
/** @param iterable<Header> $headers */
public static function createWithHeaders(object $event, iterable $headers): self
{
return self::create($event)->withHeaders($headers);
Expand All @@ -55,7 +55,7 @@
*
* @throws HeaderNotFound
*
* @template H1 of object
* @template H1 of Header
*/
public function header(string $name): object
{
Expand All @@ -73,18 +73,18 @@
public function withHeader(object $header): self
{
$message = clone $this;
$message->headers[$header::class] = $header;

Check failure on line 76 in src/Message/Message.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Property Patchlevel\EventSourcing\Message\Message<T of object>::$headers (array<class-string, Patchlevel\EventSourcing\Message\Header>) does not accept array<class-string, object>.

return $message;
}

/** @return list<object> */
/** @return list<Header> */
public function headers(): array
{
return array_values($this->headers);
}

/** @param iterable<object> $headers */
/** @param iterable<Header> $headers */
public function withHeaders(iterable $headers): self
{
$message = clone $this;
Expand Down
Loading
Loading