Chapter 6

This commit is contained in:
Carlos Buenosvinos
2022-02-28 21:51:46 +01:00
parent a103690f18
commit d0875d3aed
11 changed files with 93 additions and 56 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Cheeper\Chapter6\Application;
interface Command
{
}

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Cheeper\Chapter6\Application;
//snippet projection
interface Projection
{
}
//end-snippet

View 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

View File

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

View File

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

View File

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

View File

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

View File

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