Skip to content

Commit 18ffc8b

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

File tree

12 files changed

+333
-8
lines changed

12 files changed

+333
-8
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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:

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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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\Infrastructure\Shared\Bus\Command\CommandHandlerInterface;
9+
use Acme\UI\Http\Rest\Presentation\Order\OrderView;
10+
use Doctrine\ORM\EntityManagerInterface;
11+
12+
class CreateOrderCommandHandler implements CommandHandlerInterface
13+
{
14+
private EntityManagerInterface $entityManager;
15+
16+
public function __construct(EntityManagerInterface $entityManager)
17+
{
18+
$this->entityManager = $entityManager;
19+
}
20+
21+
public function __invoke(CreateOrderCommand $command)
22+
{
23+
$order = Order::create($command->getNumber(), $command->getState(), $command->getTotal());
24+
25+
$this->entityManager->persist($order);
26+
$this->entityManager->flush();
27+
28+
/** @TODO Think about this line because it breaks DDD */
29+
return OrderView::create($order);
30+
}
31+
}
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+
}

src/Domain/Order/Order.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Acme\Domain\Order;
6+
7+
use Acme\Domain\AggregateRootBehaviourTrait;
8+
use Acme\Domain\AggregateRootInterface;
9+
use Acme\Domain\Shared\ValueObject\DateTime;
10+
use Ramsey\Uuid\Uuid;
11+
12+
class Order implements AggregateRootInterface
13+
{
14+
use AggregateRootBehaviourTrait;
15+
16+
private string $number;
17+
18+
private int $total;
19+
20+
private string $state;
21+
22+
private DateTime $createdAt;
23+
24+
private DateTime $updatedAt;
25+
26+
public static function create(string $number, string $state, int $total)
27+
{
28+
$order = new static();
29+
$order->setUuid(Uuid::uuid4());
30+
$order->setNumber($number);
31+
$order->setState($state);
32+
$order->setTotal($total);
33+
$order->setCreatedAt(DateTime::now());
34+
35+
return $order;
36+
}
37+
38+
public function setNumber(string $number): void
39+
{
40+
$this->number = $number;
41+
}
42+
43+
public function getNumber(): string
44+
{
45+
return $this->number;
46+
}
47+
48+
public function setTotal(int $total): void
49+
{
50+
$this->total = $total;
51+
}
52+
53+
public function getTotal(): int
54+
{
55+
return $this->total;
56+
}
57+
58+
public function setState(string $state): void
59+
{
60+
$this->state = $state;
61+
}
62+
63+
public function getState(): string
64+
{
65+
return $this->state;
66+
}
67+
68+
public function setCreatedAt(DateTime $createdAt): void
69+
{
70+
$this->createdAt = $createdAt;
71+
}
72+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
5+
<entity name="Acme\Domain\Order\Order" table="orders">
6+
<id name="uuid" type="uuid_binary" column="uuid"/>
7+
<field name="number" column="number" type="string" />
8+
<field name="state" column="state" type="string" />
9+
<field name="total" column="total" type="integer" />
10+
<field name="createdAt" column="created_at" type="datetime_immutable" />
11+
<field name="updatedAt" column="updated_at" nullable="true" type="datetime_immutable" />
12+
</entity>
13+
</doctrine-mapping>

0 commit comments

Comments
 (0)