Skip to content

Commit b04b68f

Browse files
m0sviatoslavniksynov
authored andcommitted
Following HTTP over CQRS
1 parent 58ce70d commit b04b68f

File tree

18 files changed

+568
-8
lines changed

18 files changed

+568
-8
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Acme\Domain\Order\Order:
2+
properties:
3+
uuid:
4+
identifier: true
5+
collectionOperations:
6+
create_order:
7+
method: POST
8+
path: '/orders'
9+
messenger: true
10+
input: Acme\Application\UseCase\Command\Order\CreateOrder\CreateOrderInput
11+
output: Acme\UI\Http\Rest\Presentation\Order\OrderView
12+
normalization_context:
13+
groups: [ order ]
14+
itemOperations:
15+
update_order:
16+
method: PUT
17+
path: '/orders/{uuid}'
18+
status: 202
19+
messenger: true
20+
input: Acme\Application\UseCase\Command\Order\UpdateOrder\UpdateOrderInput
21+
output: false
22+
read: false
23+
openapi_context:
24+
summary: Update order async
25+
parameters:
26+
- in: path
27+
name: uuid
28+
type: string
29+
required: true
30+
responses:
31+
202:
32+
description: Order updating in process
33+
400:
34+
description: Invalid input
35+
409:
36+
description: Conflict

config/api_platform/User/User.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ Acme\Domain\User\User:
66
get:
77
method: GET
88
filters: [ 'user.search_filter' ]
9-
output: Acme\UI\Http\Rest\Presentation\User\UserView
10-
normalization_context:
11-
groups: [ profile ]
12-
get_v2:
13-
path: v2/users
14-
method: GET
15-
query: Acme\Application\UseCase\Query\User\GetUsers\GetUsersQuery
16-
filters: [ 'user.search_filter' ]
9+
output: Acme\UI\Http\Rest\Presentation\User\UserView
10+
normalization_context:
11+
groups: [ profile ]
12+
get_v2:
13+
path: v2/users
14+
method: GET
15+
query: Acme\Application\UseCase\Query\User\GetUsers\GetUsersQuery
16+
filters: [ 'user.search_filter' ]
1717
output: Acme\UI\Http\Rest\Presentation\User\UserProfileView
1818
post:
1919
method: POST
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
doctrine:
2+
orm:
3+
mappings:
4+
Order:
5+
is_bundle: false
6+
type: xml
7+
dir: '%kernel.project_dir%/src/Infrastructure/Order/Doctrine/Orm/Mapping'
8+
prefix: 'Acme\Domain\Order'
9+
alias: Order

config/packages/security.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ security:
3636
stateless: true
3737
anonymous: true
3838

39+
api_orders:
40+
pattern: ^/api/orders
41+
stateless: true
42+
anonymous: true
43+
3944
api_secured:
4045
pattern: ^/api
4146
provider: users
@@ -67,4 +72,5 @@ security:
6772
- { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
6873
- { path: ^/api/signup, roles: IS_AUTHENTICATED_ANONYMOUSLY }
6974
- { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
75+
- { path: ^/api/orders, roles: IS_AUTHENTICATED_ANONYMOUSLY }
7076
- { path: ^/api/, roles: IS_AUTHENTICATED_FULLY }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\CreateOrder;
6+
7+
use Acme\Infrastructure\Shared\Bus\Command\CommandInterface;
8+
9+
class CreateOrderCommand implements CommandInterface
10+
{
11+
private string $number;
12+
private string $state;
13+
private int $total;
14+
15+
public function __construct(string $number, string $state, int $total)
16+
{
17+
$this->number = $number;
18+
$this->state = $state;
19+
$this->total = $total;
20+
}
21+
22+
public function getNumber(): string
23+
{
24+
return $this->number;
25+
}
26+
27+
public function getState(): string
28+
{
29+
return $this->state;
30+
}
31+
32+
public function getTotal(): int
33+
{
34+
return $this->total;
35+
}
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\CreateOrder;
6+
7+
use Acme\Domain\Order\Order;
8+
use Acme\Domain\Order\Repository\OrderRepositoryInterface;
9+
use Acme\Infrastructure\Shared\Bus\Command\CommandHandlerInterface;
10+
use Acme\UI\Http\Rest\Presentation\Order\OrderView;
11+
12+
class CreateOrderCommandHandler implements CommandHandlerInterface
13+
{
14+
private OrderRepositoryInterface $orderStore;
15+
16+
public function __construct(OrderRepositoryInterface $orderStore)
17+
{
18+
$this->orderStore = $orderStore;
19+
}
20+
21+
public function __invoke(CreateOrderCommand $command)
22+
{
23+
$order = Order::create($command->getNumber(), $command->getState(), $command->getTotal());
24+
25+
$this->orderStore->store($order);
26+
27+
/** @TODO Think about this line because it breaks DDD */
28+
return OrderView::create($order);
29+
}
30+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\CreateOrder;
6+
7+
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
8+
use ApiPlatform\Core\Validator\ValidatorInterface;
9+
10+
class CreateOrderDataTransformer implements DataTransformerInterface
11+
{
12+
private ValidatorInterface $validator;
13+
14+
public function __construct(ValidatorInterface $validator)
15+
{
16+
$this->validator = $validator;
17+
}
18+
19+
public function transform($object, string $to, array $context = [])
20+
{
21+
if (!$object instanceof CreateOrderInput) {
22+
throw new \InvalidArgumentException(\sprintf('Object is not an instance of %s', CreateOrderInput::class));
23+
}
24+
25+
$this->validator->validate($object, $context);
26+
27+
return new CreateOrderCommand(
28+
$object->number,
29+
$object->state,
30+
$object->total
31+
);
32+
}
33+
34+
public function supportsTransformation($data, string $to, array $context = []): bool
35+
{
36+
return CreateOrderInput::class === ($context['input']['class'] ?? null);
37+
}
38+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\CreateOrder;
6+
7+
use Symfony\Component\Validator\Constraints as Assert;
8+
9+
class CreateOrderInput
10+
{
11+
/**
12+
* @Assert\NotBlank
13+
* @Assert\Type("string")
14+
*/
15+
public string $number;
16+
17+
/**
18+
* @Assert\NotBlank
19+
* @Assert\Type("string")
20+
*/
21+
public string $state;
22+
23+
/**
24+
* @Assert\NotBlank
25+
* @Assert\Type("int")
26+
*/
27+
public int $total;
28+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\UpdateOrder;
6+
7+
use Acme\Infrastructure\Shared\Bus\Command\CommandInterface;
8+
use Ramsey\Uuid\UuidInterface;
9+
10+
class UpdateOrderCommand implements CommandInterface
11+
{
12+
private UuidInterface $uuid;
13+
14+
private string $number;
15+
16+
private string $state;
17+
18+
private int $total;
19+
20+
public function getUuid(): UuidInterface
21+
{
22+
return $this->uuid;
23+
}
24+
25+
public function setUuid(UuidInterface $uuid): void
26+
{
27+
$this->uuid = $uuid;
28+
}
29+
30+
public function getNumber(): string
31+
{
32+
return $this->number;
33+
}
34+
35+
public function setNumber(string $number): void
36+
{
37+
$this->number = $number;
38+
}
39+
40+
public function getState(): string
41+
{
42+
return $this->state;
43+
}
44+
45+
public function setState(string $state): void
46+
{
47+
$this->state = $state;
48+
}
49+
50+
public function getTotal(): int
51+
{
52+
return $this->total;
53+
}
54+
55+
public function setTotal(int $total): void
56+
{
57+
$this->total = $total;
58+
}
59+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Application\UseCase\Command\Order\UpdateOrder;
6+
7+
use Acme\Domain\Order\Repository\OrderRepositoryInterface;
8+
use Acme\Domain\Shared\Query\Exception\NotFoundException;
9+
use Acme\Domain\Shared\ValueObject\DateTime;
10+
use Acme\Infrastructure\Shared\Bus\Command\CommandHandlerInterface;
11+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
12+
13+
class UpdateOrderCommandHandler implements CommandHandlerInterface
14+
{
15+
private OrderRepositoryInterface $orderStore;
16+
17+
public function __construct(OrderRepositoryInterface $orderStore)
18+
{
19+
$this->orderStore = $orderStore;
20+
}
21+
22+
public function __invoke(UpdateOrderCommand $command)
23+
{
24+
try {
25+
$order = $this->orderStore->find($command->getUuid());
26+
} catch (NotFoundException $e) {
27+
throw new NotFoundHttpException($e->getMessage());
28+
}
29+
30+
$order->setNumber($command->getNumber());
31+
$order->setState($command->getState());
32+
$order->setTotal($command->getTotal());
33+
$order->setUpdatedAt(DateTime::now());
34+
35+
$this->orderStore->store($order);
36+
}
37+
}

0 commit comments

Comments
 (0)