Step 00 — Squelette + intégration naïve
3 Bounded Contexts (Sales, Invoicing, LegacyFulfillment) avec : - Domaines complets (agrégats, VOs, événements, invariants) - Couche application (commands, queries, ports) - Infrastructure in-memory (repos, gateway fake) - Controllers HTTP Symfony - Couplage naïf synchrone entre BC via NaiveSalesEventPublisher - 20 tests unitaires et d'intégration passants
This commit is contained in:
12
src/Sales/Application/Command/CancelOrder.php
Normal file
12
src/Sales/Application/Command/CancelOrder.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
final readonly class CancelOrder
|
||||
{
|
||||
public function __construct(
|
||||
public string $orderId,
|
||||
) {}
|
||||
}
|
||||
31
src/Sales/Application/Command/CancelOrderHandler.php
Normal file
31
src/Sales/Application/Command/CancelOrderHandler.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
use MiniShop\Sales\Application\Port\OrderRepository;
|
||||
use MiniShop\Sales\Application\Port\SalesEventPublisher;
|
||||
use MiniShop\Sales\Domain\Event\OrderCancelled;
|
||||
use MiniShop\Sales\Domain\Model\OrderId;
|
||||
|
||||
final readonly class CancelOrderHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OrderRepository $orderRepository,
|
||||
private SalesEventPublisher $publisher,
|
||||
) {}
|
||||
|
||||
public function __invoke(CancelOrder $command): void
|
||||
{
|
||||
$order = $this->orderRepository->get(OrderId::fromString($command->orderId));
|
||||
$order->cancel();
|
||||
$this->orderRepository->save($order);
|
||||
|
||||
foreach ($order->releaseEvents() as $event) {
|
||||
if ($event instanceof OrderCancelled) {
|
||||
$this->publisher->publishOrderCancelled($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/Sales/Application/Command/ConfirmOrder.php
Normal file
12
src/Sales/Application/Command/ConfirmOrder.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
final readonly class ConfirmOrder
|
||||
{
|
||||
public function __construct(
|
||||
public string $orderId,
|
||||
) {}
|
||||
}
|
||||
31
src/Sales/Application/Command/ConfirmOrderHandler.php
Normal file
31
src/Sales/Application/Command/ConfirmOrderHandler.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
use MiniShop\Sales\Application\Port\OrderRepository;
|
||||
use MiniShop\Sales\Application\Port\SalesEventPublisher;
|
||||
use MiniShop\Sales\Domain\Event\OrderConfirmed;
|
||||
use MiniShop\Sales\Domain\Model\OrderId;
|
||||
|
||||
final readonly class ConfirmOrderHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OrderRepository $orderRepository,
|
||||
private SalesEventPublisher $publisher,
|
||||
) {}
|
||||
|
||||
public function __invoke(ConfirmOrder $command): void
|
||||
{
|
||||
$order = $this->orderRepository->get(OrderId::fromString($command->orderId));
|
||||
$order->confirm();
|
||||
$this->orderRepository->save($order);
|
||||
|
||||
foreach ($order->releaseEvents() as $event) {
|
||||
if ($event instanceof OrderConfirmed) {
|
||||
$this->publisher->publishOrderConfirmed($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/Sales/Application/Command/PlaceOrder.php
Normal file
17
src/Sales/Application/Command/PlaceOrder.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
final readonly class PlaceOrder
|
||||
{
|
||||
/**
|
||||
* @param list<array{productName: string, quantity: int, unitPriceInCents: int, currency: string}> $lines
|
||||
*/
|
||||
public function __construct(
|
||||
public string $orderId,
|
||||
public string $customerId,
|
||||
public array $lines,
|
||||
) {}
|
||||
}
|
||||
51
src/Sales/Application/Command/PlaceOrderHandler.php
Normal file
51
src/Sales/Application/Command/PlaceOrderHandler.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Command;
|
||||
|
||||
use MiniShop\Sales\Application\Port\OrderRepository;
|
||||
use MiniShop\Sales\Application\Port\SalesEventPublisher;
|
||||
use MiniShop\Sales\Domain\Event\OrderPlaced;
|
||||
use MiniShop\Sales\Domain\Model\CustomerId;
|
||||
use MiniShop\Sales\Domain\Model\Money;
|
||||
use MiniShop\Sales\Domain\Model\Order;
|
||||
use MiniShop\Sales\Domain\Model\OrderId;
|
||||
use MiniShop\Sales\Domain\Model\OrderLine;
|
||||
use MiniShop\Shared\Technical\Clock;
|
||||
|
||||
final readonly class PlaceOrderHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OrderRepository $orderRepository,
|
||||
private SalesEventPublisher $publisher,
|
||||
private Clock $clock,
|
||||
) {}
|
||||
|
||||
public function __invoke(PlaceOrder $command): void
|
||||
{
|
||||
$lines = array_map(
|
||||
static fn (array $line): OrderLine => new OrderLine(
|
||||
$line['productName'],
|
||||
$line['quantity'],
|
||||
new Money($line['unitPriceInCents'], $line['currency']),
|
||||
),
|
||||
$command->lines,
|
||||
);
|
||||
|
||||
$order = Order::place(
|
||||
OrderId::fromString($command->orderId),
|
||||
CustomerId::fromString($command->customerId),
|
||||
$lines,
|
||||
$this->clock->now(),
|
||||
);
|
||||
|
||||
$this->orderRepository->save($order);
|
||||
|
||||
foreach ($order->releaseEvents() as $event) {
|
||||
if ($event instanceof OrderPlaced) {
|
||||
$this->publisher->publishOrderPlaced($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/Sales/Application/Port/OrderRepository.php
Normal file
19
src/Sales/Application/Port/OrderRepository.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Port;
|
||||
|
||||
use MiniShop\Sales\Domain\Model\CustomerId;
|
||||
use MiniShop\Sales\Domain\Model\Order;
|
||||
use MiniShop\Sales\Domain\Model\OrderId;
|
||||
|
||||
interface OrderRepository
|
||||
{
|
||||
public function save(Order $order): void;
|
||||
|
||||
public function get(OrderId $id): Order;
|
||||
|
||||
/** @return list<Order> */
|
||||
public function findByCustomer(CustomerId $customerId): array;
|
||||
}
|
||||
18
src/Sales/Application/Port/SalesEventPublisher.php
Normal file
18
src/Sales/Application/Port/SalesEventPublisher.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Port;
|
||||
|
||||
use MiniShop\Sales\Domain\Event\OrderCancelled;
|
||||
use MiniShop\Sales\Domain\Event\OrderConfirmed;
|
||||
use MiniShop\Sales\Domain\Event\OrderPlaced;
|
||||
|
||||
interface SalesEventPublisher
|
||||
{
|
||||
public function publishOrderPlaced(OrderPlaced $event): void;
|
||||
|
||||
public function publishOrderConfirmed(OrderConfirmed $event): void;
|
||||
|
||||
public function publishOrderCancelled(OrderCancelled $event): void;
|
||||
}
|
||||
12
src/Sales/Application/Query/GetOrderById.php
Normal file
12
src/Sales/Application/Query/GetOrderById.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Query;
|
||||
|
||||
final readonly class GetOrderById
|
||||
{
|
||||
public function __construct(
|
||||
public string $orderId,
|
||||
) {}
|
||||
}
|
||||
21
src/Sales/Application/Query/GetOrderByIdHandler.php
Normal file
21
src/Sales/Application/Query/GetOrderByIdHandler.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Query;
|
||||
|
||||
use MiniShop\Sales\Application\Port\OrderRepository;
|
||||
use MiniShop\Sales\Domain\Model\Order;
|
||||
use MiniShop\Sales\Domain\Model\OrderId;
|
||||
|
||||
final readonly class GetOrderByIdHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OrderRepository $orderRepository,
|
||||
) {}
|
||||
|
||||
public function __invoke(GetOrderById $query): Order
|
||||
{
|
||||
return $this->orderRepository->get(OrderId::fromString($query->orderId));
|
||||
}
|
||||
}
|
||||
12
src/Sales/Application/Query/ListOrdersByCustomer.php
Normal file
12
src/Sales/Application/Query/ListOrdersByCustomer.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Query;
|
||||
|
||||
final readonly class ListOrdersByCustomer
|
||||
{
|
||||
public function __construct(
|
||||
public string $customerId,
|
||||
) {}
|
||||
}
|
||||
22
src/Sales/Application/Query/ListOrdersByCustomerHandler.php
Normal file
22
src/Sales/Application/Query/ListOrdersByCustomerHandler.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MiniShop\Sales\Application\Query;
|
||||
|
||||
use MiniShop\Sales\Application\Port\OrderRepository;
|
||||
use MiniShop\Sales\Domain\Model\CustomerId;
|
||||
use MiniShop\Sales\Domain\Model\Order;
|
||||
|
||||
final readonly class ListOrdersByCustomerHandler
|
||||
{
|
||||
public function __construct(
|
||||
private OrderRepository $orderRepository,
|
||||
) {}
|
||||
|
||||
/** @return list<Order> */
|
||||
public function __invoke(ListOrdersByCustomer $query): array
|
||||
{
|
||||
return $this->orderRepository->findByCustomer(CustomerId::fromString($query->customerId));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user