Как разбиваешь слои архитектуры?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Подход к разделению слоев архитектуры в PHP Backend
При проектировании backend-приложений на PHP я придерживаюсь принципа разделения ответственности (Separation of Concerns), используя многоуровневую архитектуру. Вот мой стандартный подход:
Основные архитектурные слои
1. Доменный слой (Domain Layer)
Содержит бизнес-логику и сущности, полностью независимые от инфраструктуры.
// Пример доменной сущности
class Order {
private OrderId $id;
private CustomerId $customerId;
private OrderItems $items;
private OrderStatus $status;
public function place(): void {
if ($this->items->isEmpty()) {
throw new EmptyOrderException();
}
$this->status = OrderStatus::PLACED();
$this->recordEvent(new OrderPlaced($this->id));
}
}
2. Слой приложения (Application Layer)
Координирует выполнение use cases, выступая как посредник между внешним миром и доменом.
class PlaceOrderService {
public function __construct(
private OrderRepository $repository,
private EventDispatcher $dispatcher
) {}
public function execute(PlaceOrderCommand $command): OrderId {
$order = Order::create(
$command->customerId,
$command->items
);
$order->place();
$this->repository->save($order);
$this->dispatcher->dispatchEvents(
$order->releaseEvents()
);
return $order->getId();
}
}
3. Инфраструктурный слой (Infrastructure Layer)
Реализует технические детали: работу с БД, внешними API, кэшем, файловой системой.
class DoctrineOrderRepository implements OrderRepository {
public function __construct(
private EntityManager $em
) {}
public function save(Order $order): void {
$this->em->persist($order);
$this->em->flush();
}
}
4. Слой представления/доступа (Interface/Adapter Layer)
Обрабатывает входящие запросы (HTTP, CLI, очереди) и возвращает ответы.
class OrderController {
public function __construct(
private PlaceOrderService $service,
private OrderPresenter $presenter
) {}
public function placeOrder(Request $request): JsonResponse {
$command = new PlaceOrderCommand(
$request->get('customer_id'),
$request->get('items')
);
$orderId = $this->service->execute($command);
return $this->presenter->present($orderId);
}
}
Ключевые принципы разделения
Направление зависимостей всегда идет от внешних слоев к внутренним:
- Внутренние слои (домен) ничего не знают о внешних
- Внешние слои зависят от внутренних
Маркеры правильного разделения:
- Возможность тестировать бизнес-логику без БД и HTTP
- Замена инфраструктуры (например, MySQL на PostgreSQL) без изменения домена
- Независимое развитие слоев разными командами
Практические паттерны реализации
Для простых проектов часто использую порты-адаптеры (Hexagonal):
App/
├── Domain/ # Ядро бизнес-логики
├── Application/ # Use cases, сервисы приложения
├── Infrastructure/ # Реализации репозиториев, внешние сервисы
└── UI/ # Контроллеры, консольные команды
Для сложных систем применяю модульную организацию по бизнес-контекстам (DDD Bounded Contexts):
App/
├── OrderModule/
│ ├── Domain/
│ ├── Application/
│ └── Infrastructure/
├── CustomerModule/
│ ├── Domain/
│ └── Application/
└── Shared/
└── Kernel/ # Общая инфраструктура
Конкретные техники в PHP
Внедрение зависимостей через интерфейсы:
interface OrderRepository {
public function save(Order $order): void;
public function findById(OrderId $id): ?Order;
}
// Инфраструктурный слой реализует интерфейс из доменного
class PostgreSQLOrderRepository implements OrderRepository {
// Реализация с использованием конкретной БД
}
DTO для передачи данных между слоями:
class OrderDetailsDTO {
public function __construct(
public readonly string $orderId,
public readonly array $items,
public readonly string $status
) {}
}
Преимущества такого подхода
- Тестируемость: Домен тестируется юнит-тестами без инфраструктуры
- Поддерживаемость: Изменения в одном слое минимально затрагивают другие
- Гибкость: Легкая замена компонентов (например, переход на другой фреймворк)
- Масштабируемость: Возможность распределения слоев по разным серверам
Разделение слоев — это баланс между чистотой архитектуры и практической целесообразностью. Для стартапов иногда достаточно простой 3-слойной архитектуры (Controller-Service-Repository), тогда как для enterprise-систем требуется полноценное разделение с четкими границами контекстов.