07. Introduction of QueryBus
This commit is contained in:
26
.psalm-stubs/symfony-messenger.phpstub
Normal file
26
.psalm-stubs/symfony-messenger.phpstub
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Symfony\Component\Messenger\Stamp
|
||||
{
|
||||
/** @template T */
|
||||
final class HandledStamp implements StampInterface
|
||||
{
|
||||
/** @psam-return T */
|
||||
public function getResult() { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Messenger
|
||||
{
|
||||
trait HandleTrait
|
||||
{
|
||||
/**
|
||||
* @template T
|
||||
* @psalm-param object|Envelope $message
|
||||
* @psalm-return T|Envelope<HandledStamp>
|
||||
*/
|
||||
private function handle($message) { }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ framework:
|
||||
- doctrine_ping_connection
|
||||
- doctrine_close_connection
|
||||
- doctrine_transaction
|
||||
query_bus:
|
||||
middleware:
|
||||
- validation
|
||||
|
||||
transports:
|
||||
# https://symfony.com/doc/current/messenger.html#transport-configuration
|
||||
|
||||
@@ -14,6 +14,8 @@ services:
|
||||
Cheeper\DomainModel\Follow\FollowRepository: '@Cheeper\Infrastructure\Persistence\DoctrineOrmFollowRepository'
|
||||
Cheeper\Application\EventBus: '@Cheeper\Infrastructure\Application\SymfonyMessengerEventBus'
|
||||
Redis: '@snc_redis.default'
|
||||
Symfony\Component\Messenger\MessageBusInterface $commandBus: '@command_bus'
|
||||
Symfony\Component\Messenger\MessageBusInterface $queryBus: '@query_bus'
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
<stubs>
|
||||
<file name=".psalm-stubs/phpredis.phpstub"/>
|
||||
<file name=".psalm-stubs/symfony-messenger.phpstub"/>
|
||||
</stubs>
|
||||
|
||||
<plugins>
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace App\Controller;
|
||||
|
||||
use Cheeper\Application\CountFollowers\CountFollowersQuery;
|
||||
use Cheeper\Application\CountFollowers\CountFollowersQueryHandler;
|
||||
use Cheeper\Application\CountFollowers\CountFollowersQueryResponse;
|
||||
use Cheeper\Application\QueryBus;
|
||||
use Cheeper\DomainModel\Author\AuthorDoesNotExist;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -19,6 +21,7 @@ final class GetFollowersCountController extends AbstractController
|
||||
|
||||
public function __construct(
|
||||
private readonly CountFollowersQueryHandler $countFollowersQueryHandler,
|
||||
private readonly QueryBus $queryBus,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -50,15 +53,16 @@ final class GetFollowersCountController extends AbstractController
|
||||
$this->assertValidUuid($authorId);
|
||||
|
||||
try {
|
||||
$count = ($this->countFollowersQueryHandler)(
|
||||
$response = $this->queryBus->askFor(
|
||||
new CountFollowersQuery($authorId)
|
||||
);
|
||||
assert($response instanceof CountFollowersQueryResponse);
|
||||
} catch (AuthorDoesNotExist $e) {
|
||||
throw $this->createNotFoundException($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'count' => $count,
|
||||
'count' => $response->totalNumberOfFollowers,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\CountFollowers;
|
||||
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler]
|
||||
final class CountFollowersQueryHandler
|
||||
{
|
||||
public function __construct(
|
||||
@@ -11,10 +14,12 @@ final class CountFollowersQueryHandler
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(CountFollowersQuery $query): int
|
||||
public function __invoke(CountFollowersQuery $query): CountFollowersQueryResponse
|
||||
{
|
||||
$totalFollowers = $this->redis->get("followers_of:" . $query->authorId);
|
||||
|
||||
return false === $totalFollowers ? 0 : (int)$totalFollowers;
|
||||
return new CountFollowersQueryResponse(
|
||||
false === $totalFollowers ? 0 : (int)$totalFollowers
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\CountFollowers;
|
||||
|
||||
use Cheeper\Application\QueryResponse;
|
||||
|
||||
/** @psalm-immutable */
|
||||
final class CountFollowersQueryResponse implements QueryResponse
|
||||
{
|
||||
public function __construct(
|
||||
public readonly int $totalNumberOfFollowers
|
||||
) {
|
||||
}
|
||||
}
|
||||
18
src/Cheeper/Application/QueryBus.php
Normal file
18
src/Cheeper/Application/QueryBus.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application;
|
||||
|
||||
/**
|
||||
* @template TQuery of Query
|
||||
* @template TQueryResponse of QueryResponse
|
||||
*/
|
||||
interface QueryBus
|
||||
{
|
||||
/**
|
||||
* @psalm-param TQuery $query
|
||||
* @psalm-return TQueryResponse
|
||||
*/
|
||||
public function askFor(Query $query): QueryResponse;
|
||||
}
|
||||
10
src/Cheeper/Application/QueryResponse.php
Normal file
10
src/Cheeper/Application/QueryResponse.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application;
|
||||
|
||||
/** @psalm-immutable */
|
||||
interface QueryResponse
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Infrastructure\Application;
|
||||
|
||||
use Cheeper\Application\Query;
|
||||
use Cheeper\Application\QueryBus;
|
||||
use Cheeper\Application\QueryResponse;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
use Symfony\Component\Messenger\Exception\LogicException;
|
||||
use Symfony\Component\Messenger\HandleTrait;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Messenger\Stamp\HandledStamp;
|
||||
|
||||
final class SymfonyMessengerQueryBus implements QueryBus
|
||||
{
|
||||
use HandleTrait;
|
||||
|
||||
public function __construct(
|
||||
MessageBusInterface $queryBus
|
||||
) {
|
||||
$this->messageBus = $queryBus;
|
||||
}
|
||||
|
||||
public function askFor(Query $query): QueryResponse
|
||||
{
|
||||
$result = $this->handle($query);
|
||||
assert($result instanceof QueryResponse);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -98,10 +98,10 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
|
||||
Iter\apply($events, ($this->authorWasFollowedEventHandler)(...));
|
||||
|
||||
$totalNumberOfFollowers = ($this->countFollowersQueryHandler)(
|
||||
$queryResponse = ($this->countFollowersQueryHandler)(
|
||||
new CountFollowersQuery($authorId)
|
||||
);
|
||||
|
||||
$this->assertSame(3, $totalNumberOfFollowers);
|
||||
$this->assertSame(3, $queryResponse->totalNumberOfFollowers);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user