Skip to content

Commit

Permalink
Merge pull request #53 from yceruto/cqs_improvements
Browse files Browse the repository at this point in the history
Improve CQS System
mtarld authored Dec 27, 2023
2 parents 97fee96 + 4df5705 commit e3a90e1
Showing 39 changed files with 121 additions and 50 deletions.
3 changes: 3 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -19,6 +19,9 @@
],
'declare_strict_types' => true,
'nullable_type_declaration_for_default_null_value' => false,
'phpdoc_to_comment' => [
'ignored_tags' => ['var'],
],
])
->setRiskyAllowed(true)
->setFinder($finder)
6 changes: 6 additions & 0 deletions config/packages/framework.php
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

declare(strict_types=1);

use App\BookStore\Domain\Exception\MissingBookException;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
@@ -20,6 +21,11 @@
'php_errors' => [
'log' => 4096,
],
'exceptions' => [
MissingBookException::class => [
'status_code' => 404,
],
],
],
);
if ('test' === $containerConfigurator->env()) {
3 changes: 3 additions & 0 deletions src/BookStore/Application/Command/AnonymizeBooksCommand.php
Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@

use App\Shared\Application\Command\CommandInterface;

/**
* @implements CommandInterface<void>
*/
final readonly class AnonymizeBooksCommand implements CommandInterface
{
public function __construct(
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Author;
use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;

final readonly class AnonymizeBooksCommandHandler implements CommandHandlerInterface
#[AsCommandHandler]
final readonly class AnonymizeBooksCommandHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
4 changes: 4 additions & 0 deletions src/BookStore/Application/Command/CreateBookCommand.php
Original file line number Diff line number Diff line change
@@ -4,13 +4,17 @@

namespace App\BookStore\Application\Command;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
use App\BookStore\Domain\ValueObject\BookDescription;
use App\BookStore\Domain\ValueObject\BookName;
use App\BookStore\Domain\ValueObject\Price;
use App\Shared\Application\Command\CommandInterface;

/**
* @implements CommandInterface<Book>
*/
final readonly class CreateBookCommand implements CommandInterface
{
public function __construct(
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;

final readonly class CreateBookCommandHandler implements CommandHandlerInterface
#[AsCommandHandler]
final readonly class CreateBookCommandHandler
{
public function __construct(private readonly BookRepositoryInterface $bookRepository)
{
3 changes: 3 additions & 0 deletions src/BookStore/Application/Command/DeleteBookCommand.php
Original file line number Diff line number Diff line change
@@ -7,6 +7,9 @@
use App\BookStore\Domain\ValueObject\BookId;
use App\Shared\Application\Command\CommandInterface;

/**
* @implements CommandInterface<void>
*/
final readonly class DeleteBookCommand implements CommandInterface
{
public function __construct(
Original file line number Diff line number Diff line change
@@ -5,9 +5,10 @@
namespace App\BookStore\Application\Command;

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;

final readonly class DeleteBookCommandHandler implements CommandHandlerInterface
#[AsCommandHandler]
final readonly class DeleteBookCommandHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
3 changes: 3 additions & 0 deletions src/BookStore/Application/Command/DiscountBookCommand.php
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@
use App\BookStore\Domain\ValueObject\Discount;
use App\Shared\Application\Command\CommandInterface;

/**
* @implements CommandInterface<void>
*/
final readonly class DiscountBookCommand implements CommandInterface
{
public function __construct(
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@

use App\BookStore\Domain\Exception\MissingBookException;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;

final readonly class DiscountBookCommandHandler implements CommandHandlerInterface
#[AsCommandHandler]
final readonly class DiscountBookCommandHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
4 changes: 4 additions & 0 deletions src/BookStore/Application/Command/UpdateBookCommand.php
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

namespace App\BookStore\Application\Command;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
use App\BookStore\Domain\ValueObject\BookDescription;
@@ -12,6 +13,9 @@
use App\BookStore\Domain\ValueObject\Price;
use App\Shared\Application\Command\CommandInterface;

/**
* @implements CommandInterface<Book>
*/
final readonly class UpdateBookCommand implements CommandInterface
{
public function __construct(
Original file line number Diff line number Diff line change
@@ -7,9 +7,10 @@
use App\BookStore\Domain\Exception\MissingBookException;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;

final readonly class UpdateBookCommandHandler implements CommandHandlerInterface
#[AsCommandHandler]
final readonly class UpdateBookCommandHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
4 changes: 4 additions & 0 deletions src/BookStore/Application/Query/FindBookQuery.php
Original file line number Diff line number Diff line change
@@ -4,9 +4,13 @@

namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\BookId;
use App\Shared\Application\Query\QueryInterface;

/**
* @implements QueryInterface<Book>
*/
final readonly class FindBookQuery implements QueryInterface
{
public function __construct(
15 changes: 11 additions & 4 deletions src/BookStore/Application/Query/FindBookQueryHandler.php
Original file line number Diff line number Diff line change
@@ -4,18 +4,25 @@

namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Exception\MissingBookException;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Query\QueryHandlerInterface;
use App\Shared\Application\Query\AsQueryHandler;

final readonly class FindBookQueryHandler implements QueryHandlerInterface
#[AsQueryHandler]
final readonly class FindBookQueryHandler
{
public function __construct(private BookRepositoryInterface $repository)
{
}

public function __invoke(FindBookQuery $query): ?Book
public function __invoke(FindBookQuery $query): Book
{
return $this->repository->ofId($query->id);
$book = $this->repository->ofId($query->id);
if (null === $book) {
throw new MissingBookException($query->id);
}

return $book;
}
}
4 changes: 4 additions & 0 deletions src/BookStore/Application/Query/FindBooksQuery.php
Original file line number Diff line number Diff line change
@@ -4,9 +4,13 @@

namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Author;
use App\Shared\Application\Query\QueryInterface;

/**
* @implements QueryInterface<BookRepositoryInterface>
*/
final readonly class FindBooksQuery implements QueryInterface
{
public function __construct(
5 changes: 3 additions & 2 deletions src/BookStore/Application/Query/FindBooksQueryHandler.php
Original file line number Diff line number Diff line change
@@ -5,9 +5,10 @@
namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Query\QueryHandlerInterface;
use App\Shared\Application\Query\AsQueryHandler;

final readonly class FindBooksQueryHandler implements QueryHandlerInterface
#[AsQueryHandler]
final readonly class FindBooksQueryHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
4 changes: 4 additions & 0 deletions src/BookStore/Application/Query/FindCheapestBooksQuery.php
Original file line number Diff line number Diff line change
@@ -4,8 +4,12 @@

namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Query\QueryInterface;

/**
* @implements QueryInterface<BookRepositoryInterface>
*/
final readonly class FindCheapestBooksQuery implements QueryInterface
{
public function __construct(public int $size = 10)
Original file line number Diff line number Diff line change
@@ -5,9 +5,10 @@
namespace App\BookStore\Application\Query;

use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\Shared\Application\Query\QueryHandlerInterface;
use App\Shared\Application\Query\AsQueryHandler;

final readonly class FindCheapestBooksQueryHandler implements QueryHandlerInterface
#[AsQueryHandler]
final readonly class FindCheapestBooksQueryHandler
{
public function __construct(private BookRepositoryInterface $bookRepository)
{
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ public function __construct(
) {
}

public static function fromModel(Book $book): static
public static function fromModel(Book $book): self
{
return new self(
$book->id()->value,
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\BookStore\Application\Command\CreateBookCommand;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
use App\BookStore\Domain\ValueObject\BookDescription;
@@ -45,7 +44,6 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
new Price($data->price),
);

/** @var Book $model */
$model = $this->commandBus->dispatch($command);

return BookResource::fromModel($model);
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
use ApiPlatform\State\ProcessorInterface;
use App\BookStore\Application\Command\DiscountBookCommand;
use App\BookStore\Application\Query\FindBookQuery;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\BookId;
use App\BookStore\Domain\ValueObject\Discount;
use App\BookStore\Infrastructure\ApiPlatform\Payload\DiscountBookPayload;
@@ -42,7 +41,6 @@ public function process(mixed $data, Operation $operation, array $uriVariables =

$this->commandBus->dispatch($command);

/** @var Book $model */
$model = $this->queryBus->ask(new FindBookQuery($command->id));

return BookResource::fromModel($model);
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\BookStore\Application\Command\UpdateBookCommand;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
use App\BookStore\Domain\ValueObject\BookDescription;
@@ -44,7 +43,6 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
null !== $data->price ? new Price($data->price) : null,
);

/** @var Book $model */
$model = $this->commandBus->dispatch($command);

return BookResource::fromModel($model);
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
use ApiPlatform\State\Pagination\Pagination;
use ApiPlatform\State\ProviderInterface;
use App\BookStore\Application\Query\FindBooksQuery;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Infrastructure\ApiPlatform\Resource\BookResource;
use App\Shared\Application\Query\QueryBusInterface;
@@ -39,7 +38,6 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
$limit = $this->pagination->getLimit($operation, $context);
}

/** @var BookRepositoryInterface $models */
$models = $this->queryBus->ask(new FindBooksQuery(null !== $author ? new Author($author) : null, $offset, $limit));

$resources = [];
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\BookStore\Application\Query\FindBookQuery;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\BookId;
use App\BookStore\Infrastructure\ApiPlatform\Resource\BookResource;
use App\Shared\Application\Query\QueryBusInterface;
@@ -28,9 +27,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
/** @var string $id */
$id = $uriVariables['id'];

/** @var Book|null $model */
$model = $this->queryBus->ask(new FindBookQuery(new BookId(Uuid::fromString($id))));

return null !== $model ? BookResource::fromModel($model) : null;
return BookResource::fromModel($model);
}
}
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\BookStore\Application\Query\FindCheapestBooksQuery;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Infrastructure\ApiPlatform\Resource\BookResource;
use App\Shared\Application\Query\QueryBusInterface;

@@ -25,7 +24,6 @@ public function __construct(private QueryBusInterface $queryBus)
*/
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
{
/** @var BookRepositoryInterface $models */
$models = $this->queryBus->ask(new FindCheapestBooksQuery());

$resources = [];
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

namespace App\Shared\Application\Command;

interface CommandHandlerInterface
#[\Attribute(\Attribute::TARGET_CLASS)]
class AsCommandHandler
{
}
7 changes: 7 additions & 0 deletions src/Shared/Application/Command/CommandBusInterface.php
Original file line number Diff line number Diff line change
@@ -6,5 +6,12 @@

interface CommandBusInterface
{
/**
* @template T
*
* @param CommandInterface<T> $command
*
* @return T
*/
public function dispatch(CommandInterface $command): mixed;
}
3 changes: 3 additions & 0 deletions src/Shared/Application/Command/CommandInterface.php
Original file line number Diff line number Diff line change
@@ -4,6 +4,9 @@

namespace App\Shared\Application\Command;

/**
* @template T
*/
interface CommandInterface
{
}
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

namespace App\Shared\Application\Query;

interface QueryHandlerInterface
#[\Attribute(\Attribute::TARGET_CLASS)]
class AsQueryHandler
{
}
7 changes: 7 additions & 0 deletions src/Shared/Application/Query/QueryBusInterface.php
Original file line number Diff line number Diff line change
@@ -6,5 +6,12 @@

interface QueryBusInterface
{
/**
* @template T
*
* @param QueryInterface<T> $query
*
* @return T
*/
public function ask(QueryInterface $query): mixed;
}
3 changes: 3 additions & 0 deletions src/Shared/Application/Query/QueryInterface.php
Original file line number Diff line number Diff line change
@@ -4,6 +4,9 @@

namespace App\Shared\Application\Query;

/**
* @template T
*/
interface QueryInterface
{
}
15 changes: 9 additions & 6 deletions src/Shared/Infrastructure/Symfony/Kernel.php
Original file line number Diff line number Diff line change
@@ -4,9 +4,10 @@

namespace App\Shared\Infrastructure\Symfony;

use App\Shared\Application\Command\CommandHandlerInterface;
use App\Shared\Application\Query\QueryHandlerInterface;
use App\Shared\Application\Command\AsCommandHandler;
use App\Shared\Application\Query\AsQueryHandler;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
@@ -33,10 +34,12 @@ protected function configureRoutes(RoutingConfigurator $routes): void

protected function build(ContainerBuilder $container): void
{
$container->registerForAutoconfiguration(QueryHandlerInterface::class)
->addTag('messenger.message_handler', ['bus' => 'query.bus']);
$container->registerAttributeForAutoconfiguration(AsQueryHandler::class, static function (ChildDefinition $definition): void {
$definition->addTag('messenger.message_handler', ['bus' => 'query.bus']);
});

$container->registerForAutoconfiguration(CommandHandlerInterface::class)
->addTag('messenger.message_handler', ['bus' => 'command.bus']);
$container->registerAttributeForAutoconfiguration(AsCommandHandler::class, static function (ChildDefinition $definition): void {
$definition->addTag('messenger.message_handler', ['bus' => 'command.bus']);
});
}
}
Original file line number Diff line number Diff line change
@@ -19,9 +19,17 @@ public function __construct(MessageBusInterface $commandBus)
$this->messageBus = $commandBus;
}

/**
* @template T
*
* @param CommandInterface<T> $command
*
* @return T
*/
public function dispatch(CommandInterface $command): mixed
{
try {
/** @var T */
return $this->handle($command);
} catch (HandlerFailedException $e) {
if ($exception = current($e->getWrappedExceptions())) {
Original file line number Diff line number Diff line change
@@ -19,9 +19,17 @@ public function __construct(MessageBusInterface $queryBus)
$this->messageBus = $queryBus;
}

/**
* @template T
*
* @param QueryInterface<T> $query
*
* @return T
*/
public function ask(QueryInterface $query): mixed
{
try {
/** @var T */
return $this->handle($query);
} catch (HandlerFailedException $e) {
if ($exception = current($e->getWrappedExceptions())) {
2 changes: 0 additions & 2 deletions tests/BookStore/Acceptance/BookCrudTest.php
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
namespace App\Tests\BookStore\Acceptance;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Domain\ValueObject\BookContent;
@@ -127,7 +126,6 @@ public function testCreateBook(): void

$id = new BookId(Uuid::fromString(str_replace('/api/books/', '', $response->toArray()['@id'])));

/** @var Book $book */
$book = static::getContainer()->get(BookRepositoryInterface::class)->ofId($id);

static::assertNotNull($book);
2 changes: 0 additions & 2 deletions tests/BookStore/Functional/FindBooksTest.php
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
namespace App\Tests\BookStore\Functional;

use App\BookStore\Application\Query\FindBooksQuery;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Author;
use App\Shared\Application\Query\QueryBusInterface;
@@ -56,7 +55,6 @@ public function testFilterBooksByAuthor(): void

static::assertCount(3, $bookRepository);

/** @var Book[] $books */
$books = $queryBus->ask(new FindBooksQuery(author: new Author('authorOne')));

static::assertCount(2, $books);
2 changes: 0 additions & 2 deletions tests/BookStore/Functional/FindCheapestBooksTest.php
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
namespace App\Tests\BookStore\Functional;

use App\BookStore\Application\Query\FindCheapestBooksQuery;
use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\Repository\BookRepositoryInterface;
use App\BookStore\Domain\ValueObject\Price;
use App\Shared\Application\Query\QueryBusInterface;
@@ -44,7 +43,6 @@ public function testReturnBooksSortedByPrice(): void
$bookRepository->save(DummyBookFactory::createBook(price: $price));
}

/** @var Book[] $cheapestBooks */
$cheapestBooks = $queryBus->ask(new FindCheapestBooksQuery(3));

$sortedPrices = [1000, 2000, 3000];
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@

namespace App\Tests\BookStore\Integration\Doctrine;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Infrastructure\Doctrine\DoctrineBookRepository;
use App\Shared\Infrastructure\Doctrine\DoctrinePaginator;
@@ -105,7 +104,6 @@ public function testWithCheapestsFirst(): void
$repository->save(DummyBookFactory::createBook(price: 2));

$prices = [];
/** @var Book $book */
foreach ($repository->withCheapestsFirst() as $book) {
$prices[] = $book->price()->amount;
}
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@

namespace App\Tests\BookStore\Integration\InMemory;

use App\BookStore\Domain\Model\Book;
use App\BookStore\Domain\ValueObject\Author;
use App\BookStore\Infrastructure\InMemory\InMemoryBookRepository;
use App\Shared\Infrastructure\InMemory\InMemoryPaginator;
@@ -76,7 +75,6 @@ public function testWithCheapestsFirst(): void
$repository->save(DummyBookFactory::createBook(price: 2));

$prices = [];
/** @var Book $book */
foreach ($repository->withCheapestsFirst() as $book) {
$prices[] = $book->price()->amount;
}

0 comments on commit e3a90e1

Please sign in to comment.