[Chapter 8] Improvements
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Command;
|
||||
|
||||
use Cheeper\Chapter7\Application\Command;
|
||||
use Cheeper\Chapter7\Application\MessageTrait;
|
||||
|
||||
final class NotifyToAuthorAboutNewFollowerCommand implements Command
|
||||
{
|
||||
use MessageTrait;
|
||||
|
||||
private function __construct(
|
||||
private string $fromAuthorId,
|
||||
private string $toAuthorId
|
||||
) {
|
||||
$this->stampAsNewMessage();
|
||||
}
|
||||
|
||||
public function fromAuthorId(): string
|
||||
{
|
||||
return $this->fromAuthorId;
|
||||
}
|
||||
|
||||
public function toAuthorId(): string
|
||||
{
|
||||
return $this->toAuthorId;
|
||||
}
|
||||
|
||||
public function followId(): string
|
||||
{
|
||||
return $this->followId;
|
||||
}
|
||||
|
||||
public static function fromArray(array $array): self
|
||||
{
|
||||
return new self(
|
||||
$array['from_author_id'] ?? '',
|
||||
$array['to_author_id'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Command;
|
||||
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorId;
|
||||
use Cheeper\Chapter7\DomainModel\Author\Author;
|
||||
use Cheeper\Chapter7\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\Chapter7\DomainModel\Notifier\Notifier;
|
||||
|
||||
final class NotifyToAuthorAboutNewFollowerCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private AuthorRepository $authors,
|
||||
private Notifier $notifier
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(NotifyToAuthorAboutNewFollowerCommand $command): void
|
||||
{
|
||||
$authorId = AuthorId::fromString($command->toAuthorId());
|
||||
$author = $this->authors->ofId($authorId);
|
||||
$this->checkAuthorExist($author, $authorId);
|
||||
|
||||
$this->notifier->notify($author);
|
||||
}
|
||||
|
||||
private function checkAuthorExist(?Author $author, AuthorId $authorId): void
|
||||
{
|
||||
if (null === $author) {
|
||||
throw AuthorDoesNotExist::withAuthorIdOf($authorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Command;
|
||||
|
||||
use Cheeper\Chapter7\Application\Command;
|
||||
use Cheeper\Chapter7\Application\MessageTrait;
|
||||
|
||||
final class WelcomeCommand implements Command
|
||||
{
|
||||
use MessageTrait;
|
||||
|
||||
private function __construct(
|
||||
private string $authorId,
|
||||
) {
|
||||
$this->stampAsNewMessage();
|
||||
}
|
||||
|
||||
public static function ofAuthorId(string $authorId): self
|
||||
{
|
||||
return new self($authorId);
|
||||
}
|
||||
|
||||
public function authorId(): string
|
||||
{
|
||||
return $this->authorId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Command;
|
||||
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorId;
|
||||
use Cheeper\Chapter7\DomainModel\Author\Author;
|
||||
use Cheeper\Chapter7\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\Chapter7\DomainModel\Notifier\Notifier;
|
||||
|
||||
final class WelcomeCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private AuthorRepository $authors,
|
||||
private Notifier $notifier
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(WelcomeCommand $command): void
|
||||
{
|
||||
$authorId = AuthorId::fromString($command->authorId());
|
||||
$author = $this->authors->ofId($authorId);
|
||||
$this->checkAuthorExist($author, $authorId);
|
||||
$this->notifier->notify($author);
|
||||
}
|
||||
|
||||
private function checkAuthorExist(?Author $author, AuthorId $authorId): void
|
||||
{
|
||||
if (null === $author) {
|
||||
throw AuthorDoesNotExist::withAuthorIdOf($authorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Event\AuthorFollowed;
|
||||
|
||||
use Cheeper\Chapter7\Application\Author\Projection\IncrementCountFollowersProjection;
|
||||
use Cheeper\Chapter7\Application\ProjectionBus;
|
||||
use Cheeper\Chapter7\DomainModel\Follow\AuthorFollowed;
|
||||
|
||||
//snippet snippet
|
||||
final class WhenAuthorFollowedThenIncrementFollowersProjectionEventHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectionBus $projectionBus
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(AuthorFollowed $event): void
|
||||
{
|
||||
$this->projectionBus->project(
|
||||
IncrementCountFollowersProjection::ofAuthor(
|
||||
$event->toAuthorId()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Event\AuthorFollowed;
|
||||
|
||||
use Cheeper\Chapter7\Application\Author\Command\NotifyToAuthorAboutNewFollowerCommand;
|
||||
use Cheeper\Chapter7\Application\CommandBus;
|
||||
use Cheeper\Chapter7\DomainModel\Follow\AuthorFollowed;
|
||||
|
||||
//snippet snippet
|
||||
final class WhenAuthorFollowedThenNotifyAuthorEventHandler
|
||||
{
|
||||
public function __construct(
|
||||
private CommandBus $commandBus
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(AuthorFollowed $event): void
|
||||
{
|
||||
$this->commandBus->handle(
|
||||
NotifyToAuthorAboutNewFollowerCommand::fromArray([
|
||||
'from_author_id' => $event->fromAuthorId(),
|
||||
'to_author_id' => $event->toAuthorId(),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -19,8 +19,19 @@ final class AuthorFollowedEventHandler
|
||||
public function __invoke(AuthorFollowed $event): void
|
||||
{
|
||||
$this->projectionHandler->__invoke(
|
||||
CountFollowersProjection::ofAuthor($event->toAuthorId())
|
||||
CountFollowersProjection::ofAuthor(
|
||||
$event->toAuthorId()
|
||||
)
|
||||
);
|
||||
|
||||
// Other actions, like welcoming the new author,
|
||||
// can be added here. Alternatively, a more
|
||||
// scalable design is to create one Event Handler
|
||||
// for each of the needed actions to happen in
|
||||
// reaction. All of those listening to the same
|
||||
// AuthorFollowed Domain Event.
|
||||
// @see: WhenAuthorFollowedThenCreateFollowersCounterProjectionEventHandler
|
||||
// @see: WhenAuthorFollowedThenWelcomeAuthorEventHandler
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
//end-snippet
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Event\NewAuthorSigned;
|
||||
|
||||
use Cheeper\Chapter7\Application\Author\Projection\CountFollowersProjection;
|
||||
use Cheeper\Chapter7\Application\Author\Projection\CountFollowersProjectionHandlerInterface;
|
||||
use Cheeper\Chapter7\DomainModel\Author\NewAuthorSigned;
|
||||
|
||||
//snippet author-followed-event-handler
|
||||
final class WhenNewAuthorSignedThenCreateEmptyFollowersCounterProjectionEventHandler
|
||||
{
|
||||
public function __construct(
|
||||
private CountFollowersProjectionHandlerInterface $projectionHandler
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(NewAuthorSigned $event): void
|
||||
{
|
||||
$this->projectionHandler->__invoke(
|
||||
CountFollowersProjection::ofAuthor($event->authorId())
|
||||
);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Event\AuthorFollowed;
|
||||
|
||||
use Cheeper\Chapter7\Application\Author\Command\WelcomeCommand;
|
||||
use Cheeper\Chapter7\Application\CommandBus;
|
||||
use Cheeper\Chapter7\DomainModel\Author\NewAuthorSigned;
|
||||
|
||||
//snippet snippet
|
||||
final class WhenNewAuthorSignedThenWelcomeAuthorEventHandler
|
||||
{
|
||||
public function __construct(
|
||||
private CommandBus $commandBus
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(NewAuthorSigned $event): void
|
||||
{
|
||||
$this->commandBus->handle(
|
||||
WelcomeCommand::ofAuthorId($event->authorId())
|
||||
);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -24,6 +24,15 @@ final class NewAuthorSignedEventHandler
|
||||
$event->authorUsername()
|
||||
)
|
||||
);
|
||||
|
||||
// Other actions, like welcoming the new author,
|
||||
// can be added here. Alternatively, a more
|
||||
// scalable design is to create one Event Handler
|
||||
// for each of the needed actions to happen in
|
||||
// reaction. All of those listening to the same
|
||||
// NewAuthorSigned Domain Event.
|
||||
// @see: WhenNewAuthorSignedThenCreateEmptyFollowersCounterProjectionEventHandler
|
||||
// @see: WhenNewAuthorSignedThenWelcomeAuthorEventHandler
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\Application\Author\Projection;
|
||||
|
||||
use Cheeper\Chapter7\Application\MessageTrait;
|
||||
use Cheeper\Chapter7\Application\Projection;
|
||||
|
||||
//snippet snippet
|
||||
final class IncrementCountFollowersProjection implements Projection
|
||||
{
|
||||
use MessageTrait;
|
||||
|
||||
public static function ofAuthor(string $authorId): self
|
||||
{
|
||||
return new self($authorId);
|
||||
}
|
||||
|
||||
private function __construct(
|
||||
private string $authorId
|
||||
) {
|
||||
$this->stampAsNewMessage();
|
||||
}
|
||||
|
||||
public function authorId(): string
|
||||
{
|
||||
return $this->authorId;
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
@@ -2,21 +2,19 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter8\Application\Author\Projection;
|
||||
namespace Cheeper\Chapter7\Application\Author\Projection;
|
||||
|
||||
use Cheeper\Chapter7\Application\Author\Projection\CountFollowersProjection;
|
||||
use Cheeper\Chapter7\Application\Author\Projection\CreateFollowersCounterProjectionHandler;
|
||||
use Redis;
|
||||
|
||||
//snippet projector-count-followers
|
||||
final class CountFollowersProjectionHandler
|
||||
//snippet snippet
|
||||
final class IncrementCountFollowersProjectionHandler
|
||||
{
|
||||
public function __construct(
|
||||
private Redis $redis
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(CountFollowersProjection $projection): void
|
||||
public function __invoke(IncrementCountFollowersProjection $projection): void
|
||||
{
|
||||
$this->redis->hIncrBy(
|
||||
sprintf(
|
||||
12
src/Cheeper/Chapter7/DomainModel/Notifier/Notifier.php
Normal file
12
src/Cheeper/Chapter7/DomainModel/Notifier/Notifier.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter7\DomainModel\Notifier;
|
||||
|
||||
use Cheeper\Chapter7\DomainModel\Author\Author;
|
||||
|
||||
interface Notifier
|
||||
{
|
||||
public function notify(Author $author): void;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter8\Application\Author\Event;
|
||||
|
||||
use Cheeper\Chapter6\Application\ProjectionBus;
|
||||
use Cheeper\Chapter7\Application\Author\Command\NotifyToAuthorAboutNewFollowerCommand;
|
||||
use Cheeper\Chapter7\Application\Author\Projection\IncrementCountFollowersProjection;
|
||||
use Cheeper\Chapter7\Application\CommandBus;
|
||||
use Cheeper\Chapter7\DomainModel\Follow\AuthorFollowed;
|
||||
|
||||
//snippet snippet
|
||||
final class AuthorFollowedEventHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectionBus $projectionBus,
|
||||
private CommandBus $commandBus,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(AuthorFollowed $event): void
|
||||
{
|
||||
$this->projectionBus->project(
|
||||
IncrementCountFollowersProjection::ofAuthor(
|
||||
$event->toAuthorId()
|
||||
)
|
||||
);
|
||||
|
||||
// Other actions, like notifying the author
|
||||
// about the new follower can be added here.
|
||||
// Alternatively, a more scalable design is
|
||||
// to create one Event Handler for each of
|
||||
// the needed actions to happen in reaction.
|
||||
// All of those listening to the same
|
||||
// AuthorFollowed Domain Event.
|
||||
// @see: WhenAuthorFollowedThenIncrementFollowersProjectionEventHandler
|
||||
// @see: WhenAuthorFollowedThenWelcomeAuthorEventHandler
|
||||
|
||||
$this->commandBus->handle(
|
||||
NotifyToAuthorAboutNewFollowerCommand::fromArray([
|
||||
'from_author_id' => $event->fromAuthorId(),
|
||||
'to_author_id' => $event->toAuthorId(),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
//end-snippet
|
||||
Reference in New Issue
Block a user