Improvements
This commit is contained in:
@@ -30,9 +30,9 @@ framework:
|
||||
|
||||
# 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
|
||||
# have an asynchronous transport. It has
|
||||
# to be synchronous so the customer can
|
||||
# get back the response.
|
||||
queries_sync: 'sync://'
|
||||
|
||||
# Dead letter box
|
||||
@@ -49,7 +49,7 @@ framework:
|
||||
|
||||
# Events can be run synchronously, however
|
||||
# all the benefits like performance and
|
||||
# transaction isolation comes when using
|
||||
# transaction isolation come when using
|
||||
# the asynchronous transport.
|
||||
Cheeper\Chapter4\DomainModel\DomainEvent: events_async
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ framework:
|
||||
|
||||
# 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
|
||||
# have an asynchronous transport. It has
|
||||
# to be synchronous so the customer can
|
||||
# get back the response.
|
||||
queries_sync: 'sync://'
|
||||
|
||||
# Projection Transports
|
||||
@@ -49,7 +49,7 @@ framework:
|
||||
failed_messages: '%env(MESSENGER_TRANSPORT_BASE_DSN)%/failed_messages'
|
||||
routing:
|
||||
# With a proper UX, almost all the commands should
|
||||
# be run asynchronously.However, some commands can
|
||||
# be run asynchronously. However, some commands can
|
||||
# be defined to run synchronously.
|
||||
Cheeper\Chapter7\Application\Author\Command\SignUpCommand: commands_sync
|
||||
Cheeper\Chapter7\Application\Author\Command\FollowCommand: commands_async
|
||||
@@ -57,7 +57,7 @@ framework:
|
||||
|
||||
# Events can be run synchronously, however
|
||||
# all the benefits like performance and
|
||||
# transaction isolation comes when using
|
||||
# transaction isolation come when using
|
||||
# the asynchronous transport.
|
||||
Cheeper\Chapter7\DomainModel\DomainEvent: events_async
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
//snippet post-cheep-command
|
||||
#[AsCommand(name: "app:post-chepp", description: "Post cheep from command line")]
|
||||
#[AsCommand(name: "app:post-chepp", description: "Post Cheep from command line")]
|
||||
final class PostCheepCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app:post-cheep';
|
||||
|
||||
@@ -57,13 +57,13 @@ final class ComplexPostCheepController extends AbstractController
|
||||
);
|
||||
|
||||
try {
|
||||
$logger->info('Executing SignUp command');
|
||||
$logger->info('Executing signup command');
|
||||
$entityManager->wrapInTransaction(function () use ($command, $postCheepHandler, $logger): void {
|
||||
($postCheepHandler)($command);
|
||||
$logger->info('SignUp command executed successfully');
|
||||
$logger->info('Signup command executed successfully');
|
||||
});
|
||||
} catch (AuthorDoesNotExist | InvalidArgumentException $exception) {
|
||||
$logger->error('SignUp command failed');
|
||||
$logger->error('Signup command failed');
|
||||
throw new BadRequestHttpException($exception->getMessage(), $exception);
|
||||
}
|
||||
//end-snippet
|
||||
|
||||
@@ -31,9 +31,9 @@ final class GetFollowersCounterController extends AbstractController
|
||||
);
|
||||
|
||||
/**
|
||||
* In case of errors like the author does not exist, you can:
|
||||
* In case of errors like the Author is not existing, you can:
|
||||
* A) Let your framework catch the Exceptions and automatically
|
||||
* generate 404 o 500
|
||||
* generate 404 or 500
|
||||
* B) Do it manually here in the Controller, using a try block
|
||||
* and multiple catch blocks
|
||||
*
|
||||
|
||||
@@ -41,7 +41,7 @@ final class FromScratch
|
||||
PostCheep::fromArray([
|
||||
'author_id' => '527cab4c-30a8-4d6a-bf7a-157910d569e5',
|
||||
'cheep_id' => '719ac125-83a9-4d6e-94da-493891b8f8b2',
|
||||
'message' => 'New cheep!',
|
||||
'message' => 'New Cheep!',
|
||||
])
|
||||
);
|
||||
//end-snippet
|
||||
|
||||
@@ -46,7 +46,7 @@ final class FromScratchWithCustomMiddleware
|
||||
PostCheep::fromArray([
|
||||
'author_id' => '527cab4c-30a8-4d6a-bf7a-157910d569e5',
|
||||
'cheep_id' => '719ac125-83a9-4d6e-94da-493891b8f8b2',
|
||||
'message' => 'New cheep!',
|
||||
'message' => 'New Cheep!',
|
||||
])
|
||||
);
|
||||
//end-snippet
|
||||
|
||||
@@ -77,7 +77,7 @@ final class Author
|
||||
public function compose(string $message): Cheep
|
||||
{
|
||||
if (!$this->id) {
|
||||
throw new \RuntimeException('Author id has not been assigned yet');
|
||||
throw new \RuntimeException('Author ID has not been assigned yet');
|
||||
}
|
||||
|
||||
return Cheep::compose($this->id, $message);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Follow
|
||||
* implementing an Observer pattern with
|
||||
* Subscribers that will publish the triggered
|
||||
* Domain Events into a queue system like
|
||||
* Rabbit. It's useful for Legacy projects
|
||||
* Rabbit. It's useful for legacy projects
|
||||
* because you can trigger any Domain Event
|
||||
* from any place in your code, not only
|
||||
* Entities.
|
||||
|
||||
@@ -31,7 +31,7 @@ final class CountFollowersQueryHandler
|
||||
|
||||
$followersCount = count($this->followRepository->toAuthorId($authorId));
|
||||
|
||||
// Other option would be with a counter method in the Repository
|
||||
// Another option would be with a counter method in the Repository
|
||||
// $followersCount = $this->followers->countOfAuthorId($authorId));
|
||||
|
||||
return new CountFollowersResponse(
|
||||
|
||||
@@ -28,10 +28,10 @@ final class CheepPostedEventHandler
|
||||
);
|
||||
|
||||
foreach ($follows as $follow) {
|
||||
// Sending the projection to be processed
|
||||
// asynchronously helps on improving
|
||||
// Sending the Projection to be processed
|
||||
// asynchronously helps improve
|
||||
// performance by distributing the tasks
|
||||
// between multiple workers
|
||||
// between multiple workers.
|
||||
$this->projectionBus->project(
|
||||
new AddCheepToTimelineProjection(
|
||||
authorId: $follow->fromAuthorId()->toString(),
|
||||
@@ -43,10 +43,10 @@ final class CheepPostedEventHandler
|
||||
)
|
||||
);
|
||||
|
||||
// This is an example on how to straightly do
|
||||
// This is a straightforward example of how to do
|
||||
// it with a Redis class instance. This approach
|
||||
// is synchronous. Depending on the case, it could
|
||||
// be the right choice.
|
||||
// be the correct choice.
|
||||
//
|
||||
// $this->redis->lPush(
|
||||
// sprintf("timelines_%s", $follow->fromAuthorId()->toString()),
|
||||
|
||||
@@ -27,12 +27,11 @@ final class AuthorFollowedEventHandler
|
||||
)
|
||||
);
|
||||
|
||||
// Other actions, like notifying the author
|
||||
// about the new follower can be added here.
|
||||
// Other actions, like notifying the Author
|
||||
// about the new Follower can be added here.
|
||||
// Alternatively, a more scalable design is
|
||||
// to create one Event Handler for each of
|
||||
// the needed actions to happen in reaction.
|
||||
// All of those listening to the same
|
||||
// to create an Event Handler for each of
|
||||
// action that has to happen per
|
||||
// AuthorFollowed Domain Event.
|
||||
// @see: WhenAuthorFollowedThenIncrementFollowersProjectionEventHandler
|
||||
// @see: WhenAuthorFollowedThenWelcomeAuthorEventHandler
|
||||
|
||||
@@ -36,12 +36,12 @@ final class Author
|
||||
// or an alternative way of instantiating
|
||||
// an empty instance of this object
|
||||
// so we can change the internal fields
|
||||
// when replaying events
|
||||
// when replaying Events.
|
||||
}
|
||||
|
||||
// The public interface of an Event Sourced
|
||||
// Entity should still be the same as shown
|
||||
// in the other chapters
|
||||
// in the other chapters.
|
||||
public static function signUp(
|
||||
AuthorId $authorId,
|
||||
UserName $userName,
|
||||
@@ -53,7 +53,7 @@ final class Author
|
||||
?BirthDate $birthDate = null
|
||||
): self {
|
||||
// Regular semantic constructors
|
||||
// still apply as a proper design
|
||||
// still apply as a proper design.
|
||||
$obj = new self();
|
||||
|
||||
$obj->authorId = $authorId->toString();
|
||||
@@ -79,7 +79,7 @@ final class Author
|
||||
|
||||
// The public interface of an Event Sourced
|
||||
// Entity should still be the same as shown
|
||||
// in the other chapters
|
||||
// in the other chapters.
|
||||
public function changeEmail(EmailAddress $newEmail)
|
||||
{
|
||||
// If an Entity method invokes an
|
||||
|
||||
@@ -32,11 +32,11 @@ final class EventSourcedAuthorRepository implements AuthorRepository
|
||||
/*
|
||||
public function ofUserName(UserName $userName): ?Author
|
||||
{
|
||||
// When doing Event Sourcing, Repositories
|
||||
// responsibility is reduced to hold
|
||||
// a finder by id, and add.
|
||||
// In Event Sourcing, a Repository's
|
||||
// responsibility is limited to providing
|
||||
// a finder by ID and an add method.
|
||||
// Other finder methods become
|
||||
// a Projection
|
||||
// a Projection.
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @Given Non Existing Author
|
||||
* @Given Non-Existent Author
|
||||
* @When Counting Followers
|
||||
* @Then Non Existing Author Exception Should Be Thrown
|
||||
* @Then Non-Existent Author Exception Should Be Thrown
|
||||
*/
|
||||
public function nonExistingAuthor(): void
|
||||
public function nonExistentAuthor(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
$this->expectExceptionMessage('Author "3409a21d-83b3-471e-a4f1-cf6748af65d2" does not exist');
|
||||
|
||||
@@ -16,11 +16,11 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @Given Non Existing Author
|
||||
* @Given Non-Existent Author
|
||||
* @When Counting Followers
|
||||
* @Then Non Existing Author Exception Should Be Thrown
|
||||
* @Then Non-Existent Author Exception Should Be Thrown
|
||||
*/
|
||||
public function nonExistingAuthor(): void
|
||||
public function nonExistentAuthor(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
$this->expectExceptionMessage('Author "3409a21d-83b3-471e-a4f1-cf6748af65d2" does not exist');
|
||||
|
||||
@@ -20,11 +20,11 @@ final class CountFollowersQueryHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @Given Non Existing Author
|
||||
* @Given Non-Existent Author
|
||||
* @When Counting Followers
|
||||
* @Then Non Existing Author Exception Should Be Thrown
|
||||
* @Then Non-Existent Author Exception Should Be Thrown
|
||||
*/
|
||||
public function nonExistingAuthor(): void
|
||||
public function nonExistentAuthor(): void
|
||||
{
|
||||
$this->expectException(AuthorDoesNotExist::class);
|
||||
$this->expectExceptionMessage('Author "3409a21d-83b3-471e-a4f1-cf6748af65d2" does not exist');
|
||||
|
||||
@@ -15,11 +15,11 @@ final class CountFollowersProjectionHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @Given Non Existing Author
|
||||
* @Given Non-Existent Author
|
||||
* @When Counting Followers
|
||||
* @Then Non Existing Author Exception Should Be Thrown
|
||||
* @Then Non-Existent Author Exception Should Be Thrown
|
||||
*/
|
||||
public function nonExistingAuthor(): void
|
||||
public function nonExistentAuthor(): void
|
||||
{
|
||||
$authorId = '1c22ed61-c305-44dd-a558-f261f434f583';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user