Chapter 6
This commit is contained in:
@@ -10,12 +10,6 @@ framework:
|
||||
- doctrine_ping_connection
|
||||
- doctrine_close_connection
|
||||
- doctrine_transaction
|
||||
projection.bus:
|
||||
default_middleware: allow_no_handlers
|
||||
middleware:
|
||||
- doctrine_ping_connection
|
||||
- doctrine_close_connection
|
||||
- doctrine_transaction
|
||||
query.bus:
|
||||
default_middleware: allow_no_handlers
|
||||
middleware:
|
||||
@@ -24,47 +18,56 @@ framework:
|
||||
default_middleware: allow_no_handlers
|
||||
middleware:
|
||||
- validation
|
||||
transports:
|
||||
async_commands: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/commands'
|
||||
sync_commands: 'sync://'
|
||||
events: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/events'
|
||||
query: 'sync://'
|
||||
failed_messages: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/failed-messages'
|
||||
projections: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/projections'
|
||||
projection.bus:
|
||||
default_middleware: allow_no_handlers
|
||||
middleware:
|
||||
- doctrine_ping_connection
|
||||
- doctrine_close_connection
|
||||
- doctrine_transaction
|
||||
|
||||
transports:
|
||||
# Command Transports
|
||||
commands_async: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/commands'
|
||||
commands_sync: 'sync://'
|
||||
|
||||
# Event Transports
|
||||
events_async: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/events'
|
||||
events_sync: 'sync://'
|
||||
|
||||
# Query Transports
|
||||
# It makes no sense to queries to
|
||||
# have an async transport. It has
|
||||
# to be sync so the customer can
|
||||
# get back the response
|
||||
queries_sync: 'sync://'
|
||||
|
||||
# Projection Transports
|
||||
projections_async: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/projections'
|
||||
projections_sync: 'sync://'
|
||||
|
||||
# Dead letter box
|
||||
failed_messages: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/failed_messages'
|
||||
|
||||
chapter7_sync_commands: 'sync://'
|
||||
chapter7_async_commands: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/chapter7_commands'
|
||||
chapter7_events: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/chapter7_events'
|
||||
chapter7_sync_queries: 'sync://'
|
||||
chapter7_sync_projections: 'sync://'
|
||||
chapter7_async_projections: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/chapter7_projections'
|
||||
chapter7_failed_messages: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/chapter7_failed_messages'
|
||||
routing:
|
||||
# Consider Signing up an Author is a sync command.
|
||||
# With a proper UX, almost all the commands should
|
||||
# be run asynchronously.
|
||||
# TODO: Review this comment
|
||||
Cheeper\Chapter7\Application\Command: commands_async
|
||||
|
||||
# Domain Events are sent to asynchronous
|
||||
# transport to be processed later.
|
||||
Cheeper\AllChapters\DomainModel\DomainEvent: [events, projections]
|
||||
# However, some commands can be defined to
|
||||
# be run synchronously.
|
||||
Cheeper\Chapter7\Application\Author\Command\SignUpCommand: commands_sync
|
||||
|
||||
# Chapter 4
|
||||
Cheeper\Chapter4\Application\Author\Command\SignUpWithoutEvents\SignUpCommand: async_commands
|
||||
# Events can be run synchronously, however
|
||||
# all the benefits like performance and
|
||||
# transaction isolation comes when using
|
||||
# the asynchronous transport.
|
||||
Cheeper\Chapter7\DomainModel\DomainEvent: events_async
|
||||
|
||||
# Chapter 5
|
||||
# Queries are executed synchronously
|
||||
# in the same request that data is requested.
|
||||
Cheeper\Chapter5\Application\Query: query
|
||||
# Queries must be synchronous. Their responses
|
||||
# need to be back to the customers.
|
||||
Cheeper\Chapter7\Application\Query: queries_sync
|
||||
|
||||
# Chapter 6
|
||||
Cheeper\Chapter6\Application\Author\Command\FollowCommand: async_commands
|
||||
|
||||
# End of Chapter 6
|
||||
|
||||
# Chapter 7
|
||||
Cheeper\Chapter7\Application\Command: chapter7_sync_commands
|
||||
Cheeper\Chapter7\DomainModel\DomainEvent: chapter7_events
|
||||
Cheeper\Chapter7\Application\Projection: chapter7_async_projections
|
||||
Cheeper\Chapter7\Application\Query: chapter7_sync_queries
|
||||
# End of Chapter 7
|
||||
# Projections should run asynchronously by default.
|
||||
# However, it's possible to run some synchronously
|
||||
# without getting the performance benefits.
|
||||
Cheeper\Chapter7\Application\Projection: projections_async
|
||||
@@ -4,8 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Application\Author\Command;
|
||||
|
||||
use Cheeper\Chapter6\Application\Command;
|
||||
|
||||
//snippet follow-command
|
||||
final class FollowCommand
|
||||
final class FollowCommand implements Command
|
||||
{
|
||||
private function __construct(
|
||||
private string $fromAuthorId,
|
||||
|
||||
@@ -4,8 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Application\Author\Projection;
|
||||
|
||||
use Cheeper\Chapter6\Application\Projection;
|
||||
|
||||
//snippet count-followers
|
||||
final class CountFollowersProjection
|
||||
final class CountFollowersProjection implements Projection
|
||||
{
|
||||
public static function ofAuthor(string $authorId): self
|
||||
{
|
||||
|
||||
9
src/Cheeper/Chapter6/Application/Command.php
Normal file
9
src/Cheeper/Chapter6/Application/Command.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Application;
|
||||
|
||||
interface Command
|
||||
{
|
||||
}
|
||||
11
src/Cheeper/Chapter6/Application/Projection.php
Normal file
11
src/Cheeper/Chapter6/Application/Projection.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Application;
|
||||
|
||||
//snippet projection
|
||||
interface Projection
|
||||
{
|
||||
}
|
||||
//end-snippet
|
||||
12
src/Cheeper/Chapter6/Application/ProjectionBus.php
Normal file
12
src/Cheeper/Chapter6/Application/ProjectionBus.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Application;
|
||||
|
||||
//snippet projection-bus
|
||||
interface ProjectionBus
|
||||
{
|
||||
public function project(Projection $projection): void;
|
||||
}
|
||||
//end-snippet
|
||||
@@ -4,16 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Chapter6\Infrastructure\Application\Author\Projection\SagaStyle;
|
||||
|
||||
use App\Messenger\CommandBus;
|
||||
use Cheeper\Chapter4\DomainModel\Author\AuthorFollowed;
|
||||
use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjection;
|
||||
use Cheeper\Chapter6\Application\ProjectionBus;
|
||||
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
|
||||
|
||||
//snippet symfony-author-followed-handler-in-saga-style
|
||||
final class SymfonyAuthorFollowedHandler implements MessageSubscriberInterface
|
||||
//snippet code
|
||||
final class SymfonyAuthorFollowedEventHandler implements MessageSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private CommandBus $commandBus
|
||||
private ProjectionBus $projectionBus
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ final class SymfonyAuthorFollowedHandler implements MessageSubscriberInterface
|
||||
|
||||
public function handlerAuthorFollowed(AuthorFollowed $event): void
|
||||
{
|
||||
$this->commandBus->handle(
|
||||
$this->projectionBus->project(
|
||||
CountFollowersProjection::ofAuthor($event->toAuthorId())
|
||||
);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjection;
|
||||
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
|
||||
|
||||
//snippet symfony-author-followed-handler
|
||||
final class SymfonyAuthorFollowedHandler implements MessageSubscriberInterface
|
||||
final class SymfonyAuthorFollowedEventHandler implements MessageSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private CountFollowersProjectionHandler $projector
|
||||
@@ -10,7 +10,7 @@ use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjection;
|
||||
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
|
||||
|
||||
//snippet symfony-author-unfollowed-handler
|
||||
final class SymfonyAuthorUnfollowedHandler implements MessageSubscriberInterface
|
||||
final class SymfonyAuthorUnfollowedEventHandler implements MessageSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private CommandBus $commandBus
|
||||
@@ -11,7 +11,7 @@ use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjectionHandl
|
||||
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
|
||||
|
||||
//snippet symfony-projector-count-followers
|
||||
final class SymfonyCountFollowersProjector implements MessageSubscriberInterface
|
||||
final class SymfonyCountFollowersProjectionHandler implements MessageSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private CountFollowersProjectionHandler $projector
|
||||
@@ -5,15 +5,12 @@ declare(strict_types=1);
|
||||
namespace Cheeper\Tests\Chapter6\Application\Author\Projection;
|
||||
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorDoesNotExist;
|
||||
use Cheeper\AllChapters\DomainModel\Author\AuthorId;
|
||||
use Cheeper\AllChapters\DomainModel\Cheep\CheepDate;
|
||||
use Cheeper\AllChapters\DomainModel\Cheep\CheepId;
|
||||
use Cheeper\AllChapters\DomainModel\Cheep\CheepMessage;
|
||||
use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjection;
|
||||
use Cheeper\Chapter6\Application\Author\Projection\CountFollowersProjectionHandler;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#snippet code
|
||||
final class CountFollowersProjectionHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
@@ -106,3 +103,4 @@ final class CountFollowersProjectionHandlerTest extends TestCase
|
||||
return $mock;
|
||||
}
|
||||
}
|
||||
#end-snippet
|
||||
Reference in New Issue
Block a user