02. Extract FollowCommand from FollowApplicationService. Done some cleanup.
This commit is contained in:
@@ -6,7 +6,6 @@ namespace App\Controller;
|
||||
|
||||
use Cheeper\Application\CountFollowersQuery;
|
||||
use Cheeper\Application\CountFollowersQueryHandler;
|
||||
use Cheeper\Application\FollowApplicationService;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -19,7 +18,6 @@ final class GetFollowersCountController extends AbstractController
|
||||
use ProtectsInvaritans;
|
||||
|
||||
public function __construct(
|
||||
private readonly FollowApplicationService $followApplicationService,
|
||||
private readonly CountFollowersQueryHandler $countFollowersQueryHandler,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Cheeper\Application\FollowApplicationService;
|
||||
use Cheeper\Application\FollowCommand;
|
||||
use Cheeper\Application\FollowCommandHandler;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Psl\Json;
|
||||
@@ -19,7 +20,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
final class PostFollowersController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly FollowApplicationService $followApplicationService,
|
||||
private readonly FollowCommandHandler $followCommandHandler,
|
||||
private readonly ValidatorInterface $validator,
|
||||
) {
|
||||
}
|
||||
@@ -94,7 +95,12 @@ final class PostFollowersController extends AbstractController
|
||||
}
|
||||
|
||||
try {
|
||||
$this->followApplicationService->followTo($data['from_author_id'], $data['to_author_id']);
|
||||
($this->followCommandHandler)(
|
||||
new FollowCommand(
|
||||
$data['from_author_id'],
|
||||
$data['to_author_id']
|
||||
)
|
||||
);
|
||||
} catch (AuthorDoesNotExist $e) {
|
||||
throw $this->createNotFoundException($e->getMessage());
|
||||
}
|
||||
|
||||
10
src/Cheeper/Application/Command.php
Normal file
10
src/Cheeper/Application/Command.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application;
|
||||
|
||||
/** @psalm-immutable */
|
||||
interface Command
|
||||
{
|
||||
}
|
||||
15
src/Cheeper/Application/FollowCommand.php
Normal file
15
src/Cheeper/Application/FollowCommand.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application;
|
||||
|
||||
/** @psalm-immutable */
|
||||
final class FollowCommand implements Command
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $fromAuthorId,
|
||||
public readonly string $toAuthorId,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -10,20 +10,20 @@ use Cheeper\DomainModel\Author\AuthorId;
|
||||
use Cheeper\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\DomainModel\Follow\FollowRepository;
|
||||
|
||||
final class FollowApplicationService
|
||||
final class FollowCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly FollowRepository $followRepository,
|
||||
private readonly AuthorRepository $authorRepository,
|
||||
) {
|
||||
private readonly FollowRepository $followRepository,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param non-empty-string $fromAuthorId
|
||||
* @psalm-param non-empty-string $toAuthorId
|
||||
*/
|
||||
public function followTo(string $fromAuthorId, string $toAuthorId): void
|
||||
public function __invoke(FollowCommand $command): void
|
||||
{
|
||||
$fromAuthorId = $command->fromAuthorId;
|
||||
$toAuthorId = $command->toAuthorId;
|
||||
|
||||
$fromAuthor = $this->tryToFindAuthor($fromAuthorId);
|
||||
$toAuthor = $this->tryToFindAuthor($toAuthorId);
|
||||
|
||||
@@ -32,21 +32,7 @@ final class FollowApplicationService
|
||||
$this->followRepository->add($follow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param non-empty-string $authorId
|
||||
*/
|
||||
public function countFollowersOf(string $authorId): int
|
||||
{
|
||||
$this->tryToFindAuthor($authorId);
|
||||
|
||||
return $this->followRepository->numberOfFollowersFor(
|
||||
AuthorId::fromString($authorId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param non-empty-string $authorId
|
||||
*/
|
||||
/** @psalm-param non-empty-string $authorId */
|
||||
private function tryToFindAuthor(string $authorId): Author
|
||||
{
|
||||
$id = AuthorId::fromString($authorId);
|
||||
@@ -64,4 +50,4 @@ final class FollowApplicationService
|
||||
throw AuthorDoesNotExist::withAuthorIdOf($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,8 @@ namespace Cheeper\Tests\Application;
|
||||
|
||||
use Cheeper\Application\CountFollowersQuery;
|
||||
use Cheeper\Application\CountFollowersQueryHandler;
|
||||
use Cheeper\Application\FollowApplicationService;
|
||||
use Cheeper\Application\FollowCommand;
|
||||
use Cheeper\Application\FollowCommandHandler;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\DomainModel\Follow\FollowRepository;
|
||||
@@ -19,7 +20,6 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
{
|
||||
private AuthorRepository $authorRepository;
|
||||
private FollowRepository $followRepository;
|
||||
private FollowApplicationService $followApplicationService;
|
||||
private CountFollowersQueryHandler $countFollowersQueryHandler;
|
||||
|
||||
protected function setUp(): void
|
||||
@@ -27,7 +27,6 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
$this->authorRepository = new InMemoryAuthorRepository();
|
||||
$this->followRepository = new InMemoryFollowRepository();
|
||||
|
||||
$this->followApplicationService = new FollowApplicationService($this->followRepository, $this->authorRepository);
|
||||
$this->countFollowersQueryHandler = new CountFollowersQueryHandler($this->authorRepository, $this->followRepository);
|
||||
}
|
||||
|
||||
@@ -59,9 +58,10 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
|
||||
$authorId = $author->authorId()->id;
|
||||
|
||||
$this->followApplicationService->followTo($follower1->authorId()->id, $authorId);
|
||||
$this->followApplicationService->followTo($follower2->authorId()->id, $authorId);
|
||||
$this->followApplicationService->followTo($follower3->authorId()->id, $authorId);
|
||||
$followCommandHandler = new FollowCommandHandler($this->authorRepository, $this->followRepository);
|
||||
($followCommandHandler)(new FollowCommand($follower1->authorId()->id, $authorId));
|
||||
($followCommandHandler)(new FollowCommand($follower2->authorId()->id, $authorId));
|
||||
($followCommandHandler)(new FollowCommand($follower3->authorId()->id, $authorId));
|
||||
|
||||
$totalNumberOfFollowers = ($this->countFollowersQueryHandler)(
|
||||
new CountFollowersQuery($authorId)
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Tests\Application;
|
||||
|
||||
use Cheeper\Application\FollowApplicationService;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\DomainModel\Follow\FollowRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryAuthorRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryFollowRepository;
|
||||
use Cheeper\Tests\DomainModel\Author\AuthorTestDataBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class FollowApplicationServiceTest extends TestCase
|
||||
{
|
||||
private AuthorRepository $authorRepository;
|
||||
private FollowApplicationService $followApplicationService;
|
||||
private FollowRepository $followRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->authorRepository = new InMemoryAuthorRepository();
|
||||
$this->followRepository = new InMemoryFollowRepository();
|
||||
|
||||
$this->followApplicationService = new FollowApplicationService($this->followRepository, $this->authorRepository);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenANonExistingAuthorWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
|
||||
$this->followApplicationService->followTo(
|
||||
AuthorTestDataBuilder::anAuthorIdentity()->id,
|
||||
AuthorTestDataBuilder::anAuthorIdentity()->id,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenANonExistingAuthorToFollowWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
|
||||
$author = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$this->authorRepository->add($author);
|
||||
|
||||
$this->followApplicationService->followTo(
|
||||
$author->authorId()->id,
|
||||
AuthorTestDataBuilder::anAuthorIdentity()->id,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenTwoExistingAuthorsWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$fromAuthor = AuthorTestDataBuilder::anAuthor()->build();
|
||||
$toAuthor = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$this->authorRepository->add($fromAuthor);
|
||||
$this->authorRepository->add($toAuthor);
|
||||
|
||||
$this->followApplicationService->followTo(
|
||||
$fromAuthor->authorId()->id,
|
||||
$toAuthor->authorId()->id
|
||||
);
|
||||
|
||||
$follows = $this->followRepository->toAuthorId($toAuthor->authorId());
|
||||
|
||||
$this->assertCount(1, $follows);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenFollowersCountForANonExistingAuthorWhenCountIsRequestedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
|
||||
$this->followApplicationService->countFollowersOf(
|
||||
AuthorTestDataBuilder::anAuthorIdentity()->id
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenFollowersCountWhenCountIsRequestedThenItShouldReturnTheTotalNumberOfFollowers(): void
|
||||
{
|
||||
$author = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$follower1 = AuthorTestDataBuilder::anAuthor()->build();
|
||||
$follower2 = AuthorTestDataBuilder::anAuthor()->build();
|
||||
$follower3 = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$this->authorRepository->add($author);
|
||||
$this->authorRepository->add($follower1);
|
||||
$this->authorRepository->add($follower2);
|
||||
$this->authorRepository->add($follower3);
|
||||
|
||||
$this->followApplicationService->followTo($follower1->authorId()->id, $author->authorId()->id);
|
||||
$this->followApplicationService->followTo($follower2->authorId()->id, $author->authorId()->id);
|
||||
$this->followApplicationService->followTo($follower3->authorId()->id, $author->authorId()->id);
|
||||
|
||||
$totalNumberOfFollowers = $this->followApplicationService->countFollowersOf($author->authorId()->id);
|
||||
|
||||
$this->assertSame(3, $totalNumberOfFollowers);
|
||||
}
|
||||
}
|
||||
80
tests/Cheeper/Tests/Application/FollowCommandHandlerTest.php
Normal file
80
tests/Cheeper/Tests/Application/FollowCommandHandlerTest.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Tests\Application;
|
||||
|
||||
use Cheeper\Application\FollowCommand;
|
||||
use Cheeper\Application\FollowCommandHandler;
|
||||
use Cheeper\DomainModel\Author\Author;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\DomainModel\Author\AuthorRepository;
|
||||
use Cheeper\DomainModel\Follow\FollowRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryAuthorRepository;
|
||||
use Cheeper\Infrastructure\Persistence\InMemoryFollowRepository;
|
||||
use Cheeper\Tests\DomainModel\Author\AuthorTestDataBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class FollowCommandHandlerTest extends TestCase
|
||||
{
|
||||
private AuthorRepository $authorRepository;
|
||||
private FollowRepository $followRepository;
|
||||
private FollowCommandHandler $followCommandHandler;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->authorRepository = new InMemoryAuthorRepository();
|
||||
$this->followRepository = new InMemoryFollowRepository();
|
||||
|
||||
$this->followCommandHandler = new FollowCommandHandler($this->authorRepository, $this->followRepository);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenANonExistingAuthorWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
|
||||
$this->follow(
|
||||
AuthorTestDataBuilder::anAuthor()->build(),
|
||||
AuthorTestDataBuilder::anAuthor()->build(),
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenANonExistingAuthorToFollowWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
|
||||
$author = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$this->authorRepository->add($author);
|
||||
|
||||
$this->follow(
|
||||
$author,
|
||||
AuthorTestDataBuilder::anAuthor()->build(),
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function givenTwoExistingAuthorsWhenFollowUseCaseIsExecutedThenAnExceptionShouldBeThrown(): void
|
||||
{
|
||||
$fromAuthor = AuthorTestDataBuilder::anAuthor()->build();
|
||||
$toAuthor = AuthorTestDataBuilder::anAuthor()->build();
|
||||
|
||||
$this->authorRepository->add($fromAuthor);
|
||||
$this->authorRepository->add($toAuthor);
|
||||
|
||||
$this->follow($fromAuthor, $toAuthor);
|
||||
|
||||
$follows = $this->followRepository->toAuthorId($toAuthor->authorId());
|
||||
|
||||
$this->assertCount(1, $follows);
|
||||
}
|
||||
|
||||
private function follow(Author $fromAuthor, Author $toAuthor): void
|
||||
{
|
||||
($this->followCommandHandler)(
|
||||
new FollowCommand($fromAuthor->authorId()->id, $toAuthor->authorId()->id)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user