Вместо сервисов на Framework Foton используются внутренние модули.
Попробуем реализовать гексагональную архитектуру DDD на примере интернет магазина и работы с заказами
/modules/ order.php # Адаптер-фасад для Foton ($this->core->obj('order')) order/ ├──── Domain/ │ ├── Order.php # Агрегат Order (сущность) │ ├── OrderItem.php # Entity (позиция заказа) │ ├── OrderId.php # Value Object │ ├── Money.php # Value Object │ ├── OrderStatus.php # Enum (перечисление статусов) │ ├── CustomerId.php # Value Object │ ├── Event/ │ │ ├── OrderPlaced.php # Доменные события │ │ └── OrderStatusChanged.php │ └── Exception/ │ ├── EmptyOrderException.php │ └── InvalidStatusTransitionException.php ├──── Port/ │ ├── In/ # Первичные порты (driving) — use case интерфейсы │ │ ├── PlaceOrderUseCase.php │ │ ├── GetOrderUseCase.php │ │ └── ChangeOrderStatusUseCase.php │ └── Out/ # Вторичные порты (driven) — инфраструктурные интерфейсы │ ├── OrderRepositoryInterface.php │ ├── ProductRepositoryInterface.php │ ├── PaymentGatewayInterface.php │ └── EventDispatcherInterface.php ├──── Application/ │ ├── Command/ │ │ ├── PlaceOrderCommand.php # Input DTO (команда) │ │ └── ChangeStatusCommand.php │ ├── Result/ │ │ └── OrderResult.php # Output DTO │ └── Service/ │ ├── PlaceOrderService.php # Use case реализация │ ├── GetOrderService.php │ └── ChangeOrderStatusService.php ├──── Adapter/ │ ├── Out/ # Secondaire адаптеры (реализации портов) │ │ ├── FotonOrderRepository.php │ │ ├── FotonProductRepository.php │ │ ├── SberPaymentGateway.php │ │ └── SyncEventDispatcher.php │ └── In/ # Primary адаптеры (веб-контроллеры, CLI) │ └── OrderController.php └──── bootstrap.php # Связывание портов с адаптерами (DIC)
Сам код здесь приводить не будем, так как его очень много, главное что нужно понимать - при правильной архитектуре абстракции всегда выше на уровень директорий чем реализации, всегда реализации должны использовать верхние уровни (абстракции) и зависеть от них, но не наоборот. В небольшой архитектуре из 2-3 файлов можно использовать такой подход, когда по сути верхний класс является подобием обертки и в зависимости от параметров вызова дергает тот или иной класс, но когда мы говорим именно про DDD очень важно соблюдать именно этот подход.