Fixes for CI & some code updates
This commit is contained in:
@@ -28,7 +28,7 @@ services:
|
||||
- ./:/var/www/html
|
||||
async-commands-consumer:
|
||||
build: .docker/php-cli
|
||||
profiles: ["async-commands"]
|
||||
profiles: [ "async-commands" ]
|
||||
init: true
|
||||
env_file: .env.docker.dist
|
||||
working_dir: /var/www/html
|
||||
@@ -37,7 +37,7 @@ services:
|
||||
- ./:/var/www/html
|
||||
async-events-consumer:
|
||||
build: .docker/php-cli
|
||||
profiles: ["async-events"]
|
||||
profiles: [ "async-events" ]
|
||||
init: true
|
||||
env_file: .env.docker.dist
|
||||
working_dir: /var/www/html
|
||||
@@ -46,7 +46,7 @@ services:
|
||||
- ./:/var/www/html
|
||||
async-projections-consumer:
|
||||
build: .docker/php-cli
|
||||
profiles: ["async-projections"]
|
||||
profiles: [ "async-projections" ]
|
||||
init: true
|
||||
env_file: .env.docker.dist
|
||||
working_dir: /var/www/html
|
||||
|
||||
@@ -10,8 +10,8 @@ use Cheeper\AllChapters\DomainModel\Author\UserName;
|
||||
//snippet authors
|
||||
interface AuthorRepository
|
||||
{
|
||||
public function ofId(AuthorId $authorId): ?Author;
|
||||
public function ofUserName(UserName $userName): ?Author;
|
||||
public function ofId(AuthorId $authorId): Author|null;
|
||||
public function ofUserName(UserName $userName): Author|null;
|
||||
public function add(Author $author): void;
|
||||
}
|
||||
//end-snippet
|
||||
|
||||
@@ -18,7 +18,7 @@ final class DoctrineOrmAuthorRepository implements AuthorRepository
|
||||
) {
|
||||
}
|
||||
|
||||
public function ofId(AuthorId $authorId): ?Author
|
||||
public function ofId(AuthorId $authorId): Author|null
|
||||
{
|
||||
return $this->em
|
||||
->getRepository(Author::class)
|
||||
@@ -27,7 +27,7 @@ final class DoctrineOrmAuthorRepository implements AuthorRepository
|
||||
]);
|
||||
}
|
||||
|
||||
public function ofUserName(UserName $userName): ?Author
|
||||
public function ofUserName(UserName $userName): Author|null
|
||||
{
|
||||
return $this->em
|
||||
->getRepository(Author::class)
|
||||
|
||||
@@ -20,20 +20,20 @@ final class InMemoryAuthorRepository implements AuthorRepository
|
||||
$this->authors = [];
|
||||
}
|
||||
|
||||
public function ofId(AuthorId $authorId): ?Author
|
||||
public function ofId(AuthorId $authorId): Author|null
|
||||
{
|
||||
$candidate = head(
|
||||
select($this->authors, fn (Author $u): bool => $u->authorId()->equals($authorId))
|
||||
);
|
||||
|
||||
if (null === $candidate) {
|
||||
return $candidate;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
public function ofUserName(UserName $userName): ?Author
|
||||
public function ofUserName(UserName $userName): Author|null
|
||||
{
|
||||
$candidate = head(
|
||||
select($this->authors, fn (Author $u): bool => $u->userName()->equalsTo($userName))
|
||||
|
||||
@@ -45,10 +45,10 @@ final class Author
|
||||
?string $location = null,
|
||||
?Website $website = null,
|
||||
?BirthDate $birthDate = null
|
||||
): static {
|
||||
): self {
|
||||
// Regular semantic constructors
|
||||
// still apply as a proper design
|
||||
$obj = new static();
|
||||
$obj = new self();
|
||||
|
||||
$obj->authorId = $authorId->toString();
|
||||
$obj->userName = $userName->userName();
|
||||
@@ -67,6 +67,11 @@ final class Author
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function authorId(): string
|
||||
{
|
||||
return $this->authorId;
|
||||
}
|
||||
|
||||
public function changeEmail(EmailAddress $newEmail)
|
||||
{
|
||||
$this->recordApplyAndPublishThat(
|
||||
|
||||
15
src/Cheeper/Chapter9/DomainModel/Author/AuthorRepository.php
Normal file
15
src/Cheeper/Chapter9/DomainModel/Author/AuthorRepository.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter9\DomainModel\Author;
|
||||
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorId;
|
||||
use Cheeper\AllChapters\DomainModel\Author\UserName;
|
||||
|
||||
interface AuthorRepository
|
||||
{
|
||||
public function ofId(AuthorId $authorId): Author|null;
|
||||
public function ofUserName(UserName $userName): Author|null;
|
||||
public function add(Author $author): void;
|
||||
}
|
||||
@@ -8,18 +8,13 @@ use Cheeper\Chapter7\DomainModel\DomainEvent;
|
||||
|
||||
class EventStream implements \Iterator
|
||||
{
|
||||
private string $aggregateId;
|
||||
/** @var DomainEvent[] */
|
||||
private array $events;
|
||||
|
||||
/**
|
||||
* @param string $aggregateId
|
||||
* @param DomainEvent[] $events
|
||||
*/
|
||||
public function __construct(string $aggregateId, array $events)
|
||||
{
|
||||
$this->aggregateId = $aggregateId;
|
||||
$this->events = $events;
|
||||
public function __construct(
|
||||
private string $aggregateId,
|
||||
private array $events
|
||||
) {
|
||||
}
|
||||
|
||||
public function getAggregateId(): string
|
||||
@@ -51,4 +46,9 @@ class EventStream implements \Iterator
|
||||
{
|
||||
return key($this->events) !== null;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return count($this->events) === 0;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter9\DomainModel;
|
||||
|
||||
//snippet post
|
||||
class Post extends AggregateRoot
|
||||
{
|
||||
//ignore
|
||||
private PostId $id;
|
||||
private ?string $title = null;
|
||||
private ?string $content = null;
|
||||
private bool $published = false;
|
||||
private array $categories = [];
|
||||
|
||||
protected function __construct(PostId $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function id(): PostId
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function title(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function content(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function categories(): array
|
||||
{
|
||||
return array_values($this->categories);
|
||||
}
|
||||
|
||||
public function isPublished(): bool
|
||||
{
|
||||
return $this->published === true;
|
||||
}
|
||||
//end-ignore
|
||||
|
||||
public static function writeNewFrom(string $title, string $content): self
|
||||
{
|
||||
$postId = PostId::create();
|
||||
|
||||
$post = new static($postId);
|
||||
|
||||
$post->recordApplyAndPublishThat(
|
||||
new PostWasCreated($postId, $title, $content)
|
||||
);
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
public function publish(): void
|
||||
{
|
||||
$this->recordApplyAndPublishThat(
|
||||
new PostWasPublished($this->id)
|
||||
);
|
||||
}
|
||||
|
||||
public function categorizeIn(CategoryId $categoryId): void
|
||||
{
|
||||
$this->recordApplyAndPublishThat(
|
||||
new PostWasCategorized($this->id, $categoryId)
|
||||
);
|
||||
}
|
||||
|
||||
public function changeContentFor(string $newContent): void
|
||||
{
|
||||
$this->recordApplyAndPublishThat(
|
||||
new PostContentWasChanged($this->id, $newContent)
|
||||
);
|
||||
}
|
||||
|
||||
public function changeTitleFor(string $newTitle): void
|
||||
{
|
||||
$this->recordApplyAndPublishThat(
|
||||
new PostTitleWasChanged($this->id, $newTitle)
|
||||
);
|
||||
}
|
||||
|
||||
protected function applyPostWasCreated(PostWasCreated $event): void
|
||||
{
|
||||
$this->id = $event->postId();
|
||||
$this->title = $event->title();
|
||||
$this->content = $event->content();
|
||||
}
|
||||
|
||||
protected function applyPostWasPublished(PostWasPublished $event): void
|
||||
{
|
||||
$this->published = true;
|
||||
}
|
||||
|
||||
protected function applyPostWasCategorized(PostWasCategorized $event): void
|
||||
{
|
||||
$this->categories[$event->categoryId()->id()] = $event->categoryId();
|
||||
}
|
||||
|
||||
protected function applyPostContentWasChanged(PostContentWasChanged $event): void
|
||||
{
|
||||
$this->content = $event->content();
|
||||
}
|
||||
|
||||
protected function applyPostTitleWasChanged(PostTitleWasChanged $event): void
|
||||
{
|
||||
$this->title = $event->title();
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -6,26 +6,28 @@ namespace Cheeper\Chapter9\Infrastructure\DomainModel\Author;
|
||||
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorId;
|
||||
use Cheeper\AllChapters\DomainModel\Author\UserName;
|
||||
use Cheeper\Chapter7\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\Chapter9\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\Chapter9\DomainModel\Author\Author;
|
||||
use Cheeper\Chapter9\DomainModel\EventStore;
|
||||
use Cheeper\Chapter9\DomainModel\EventStream;
|
||||
|
||||
//snippet code
|
||||
class EventSourcedAuthorRepository implements AuthorRepository
|
||||
final class EventSourcedAuthorRepository implements AuthorRepository
|
||||
{
|
||||
private EventStore $eventStore;
|
||||
|
||||
public function __construct(EventStore $eventStore)
|
||||
{
|
||||
$this->eventStore = $eventStore;
|
||||
public function __construct(
|
||||
private EventStore $eventStore
|
||||
) {
|
||||
}
|
||||
|
||||
public function ofId(AuthorId $authorId): Author
|
||||
public function ofId(AuthorId $authorId): Author|null
|
||||
{
|
||||
return Author::reconstitute(
|
||||
$this->eventStore->getEventsFor($authorId->id())
|
||||
);
|
||||
$eventStream = $this->eventStore->getEventsFor($authorId->id());
|
||||
|
||||
if ($eventStream->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Author::reconstitute($eventStream);
|
||||
}
|
||||
|
||||
public function ofUserName(UserName $userName): ?Author
|
||||
@@ -37,9 +39,9 @@ class EventSourcedAuthorRepository implements AuthorRepository
|
||||
|
||||
public function add(Author $author): void
|
||||
{
|
||||
$this->eventStore->append(
|
||||
newEventStream:: $author->domainEvents()
|
||||
);
|
||||
$eventStream = new EventStream($author->authorId(), $author->domainEvents());
|
||||
|
||||
$this->eventStore->append($eventStream);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -11,37 +11,23 @@ use Redis;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
//snippet code
|
||||
class RedisEventStore implements EventStore
|
||||
final class RedisEventStore implements EventStore
|
||||
{
|
||||
private Redis $redis;
|
||||
private Serializer $serializer;
|
||||
|
||||
public function __construct(
|
||||
Redis $redis,
|
||||
Serializer $serializer
|
||||
)
|
||||
{
|
||||
$this->redis = $redis;
|
||||
$this->serializer = $serializer;
|
||||
private Redis $redis,
|
||||
private Serializer $serializer,
|
||||
) {
|
||||
}
|
||||
|
||||
public function append(EventStream $eventStream): void
|
||||
{
|
||||
/** @var DomainEvent */
|
||||
foreach ($eventStream as $event) {
|
||||
$data = $this->serializer->serialize($event, 'json');
|
||||
|
||||
$date = (new \DateTimeImmutable())->format('YmdHis');
|
||||
$serializedEvent = $this->serialize($event);
|
||||
|
||||
$this->redis->rpush(
|
||||
'events:' . $eventStream->getAggregateId(),
|
||||
$this->serializer->serialize([
|
||||
'type' => get_class($event),
|
||||
'created_on' => $date,
|
||||
'data' => $data
|
||||
],
|
||||
'json'
|
||||
)
|
||||
$this->serializer->serialize($serializedEvent, 'json')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -59,20 +45,34 @@ class RedisEventStore implements EventStore
|
||||
-1
|
||||
);
|
||||
|
||||
/** @var DomainEvent[] */
|
||||
$events = [];
|
||||
|
||||
/** @var string */
|
||||
foreach ($serializedEvents as $serializedEvent) {
|
||||
$event = (array) $this->serializer->deserialize($serializedEvent);
|
||||
|
||||
$eventData = (string) $event['data'];
|
||||
|
||||
/** @var DomainEvent */
|
||||
$events[] = $this->serializer->deserialize($eventData);
|
||||
$events[] = $this->deserialize($serializedEvent);
|
||||
}
|
||||
|
||||
return new EventStream($id, $events);
|
||||
}
|
||||
|
||||
//ignore
|
||||
private function deserialize(array $event): DomainEvent
|
||||
{
|
||||
return $this->serializer->deserialize(
|
||||
$event['data'],
|
||||
$event['type'],
|
||||
'json'
|
||||
);
|
||||
}
|
||||
|
||||
private function serialize(mixed $event): array
|
||||
{
|
||||
return [
|
||||
'type' => get_class($event),
|
||||
'created_on' => (new \DateTimeImmutable())->format('YmdHis'),
|
||||
'data' => $this->serializer->serialize($event, 'json')
|
||||
];
|
||||
}
|
||||
//end-ignore
|
||||
}
|
||||
//end-snippet
|
||||
Reference in New Issue
Block a user