02. Extract FollowCommand from FollowApplicationService. Done some cleanup.

This commit is contained in:
theUniC
2022-09-02 21:35:10 +02:00
parent 03838e0239
commit bcbde3e69b
8 changed files with 130 additions and 142 deletions

View File

@@ -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,
) {
}

View File

@@ -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());
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Cheeper\Application;
/** @psalm-immutable */
interface Command
{
}

View 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,
) {
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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)

View File

@@ -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);
}
}

View 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)
);
}
}