В каком направлении строятся зависимости между слоями в микросервисной архитектуре?

Ответ

В чистой архитектуре микросервисов зависимости строятся по принципу «зависимости направлены внутрь» (Dependency Inward Rule). Это означает:

Направление зависимостей:

Инфраструктурный слой → Слой приложения → Доменный слой
      (внешний)          (промежуточный)      (внутренний)
         ↑                     ↑                    ↑
    Зависит от            Зависит от         НЕ зависит ни от кого

Конкретные примеры для PHP-микросервиса:

  1. Доменный слой (Domain Layer) – содержит бизнес-сущности и правила:

    // Order.php - сущность
    class Order {
    private string $id;
    private float $total;
    private OrderStatus $status;
    
    public function markAsPaid(): void {
        $this->status = OrderStatus::PAID;
    }
    }
  2. Слой приложения (Application Layer) – содержит use cases:

    // PayOrderHandler.php
    class PayOrderHandler {
    public function __construct(
        private OrderRepositoryInterface $repository
    ) {}
    
    public function handle(PayOrderCommand $command): void {
        $order = $this->repository->find($command->orderId);
        $order->markAsPaid();
        $this->repository->save($order);
    }
    }
  3. Инфраструктурный слой (Infrastructure Layer) – реализует интерфейсы:

    // DoctrineOrderRepository.php
    class DoctrineOrderRepository implements OrderRepositoryInterface {
    public function __construct(private EntityManagerInterface $em) {}
    
    public function find(string $id): ?Order {
        return $this->em->find(Order::class, $id);
    }
    }

Почему это важно:

  • Доменный слой не зависит от фреймворков, БД или внешних сервисов – его можно тестировать изолированно
  • Легко менять инфраструктуру – заменить Doctrine на Eloquent, REST на GraphQL, не затрагивая бизнес-логику
  • Четкое разделение ответственности – каждый слой решает свою задачу

В микросервисной архитектуре этот принцип особенно критичен, так как позволяет независимо развивать и масштабировать отдельные сервисы.

Ответ 18+ 🔞

Слушай, а вот это реально важная штука, про которую половина народа нихуя не помнит, пока в проект не вляпается. Чистая архитектура в микросервисах — это как раз тот случай, когда если накосячить с зависимостями, потом будешь ебаться с этим кодом, как дурень с писаной торбой.

Представь себе такую слоёную конструкцию, как торт «Птичье молоко». Только перевёрнутую. Главное правило — «зависимости смотрят внутрь». То есть, самый сок, ядро — это доменный слой. Он, блядь, неприкасаемый, как икона. Он нихуя не знает ни про какую базу данных, ни про какой HTTP, ни про какой фреймворк. Он просто содержит бизнес-правила, и всё. Это святое.

Дальше идёт слой приложения — это уже use cases, хендлеры команд, вся эта движуха. Он зависит от доменного слоя, потому что использует его сущности. Это логично, ёпта.

А самый внешний, инфраструктурный слой — это уже всякая техническая хуйня: работа с БД, отправка запросов куда-то, консольные команды. Этот слой зависит от слоя приложения, реализуя его интерфейсы. Получается такая матрёшка, где все зависимости заглядывают внутрь, к ядру, а не наоборот.

Вот смотри на примере, чтобы не было, как у мартышлюшки, всё в одну кучу:

  1. Доменный слой (ядро, него никто не трогает): Тут живут твои бизнес-сущности. Просто классы с данными и логикой. Никаких EntityManager или Request тут, блядь, быть не должно. Чистая правда.

    // Order.php - вот она, священная корова
    class Order {
        private string $id;
        private float $total;
        private OrderStatus $status;
    
        // Вот она, бизнес-логика. "Пометить как оплаченный". Больше ничего.
        public function markAsPaid(): void {
            $this->status = OrderStatus::PAID;
        }
    }
  2. Слой приложения (промежуточный, использует ядро): Тут живут сценарии использования. Например, «оплатить заказ». Он знает про репозиторий (интерфейс!), чтобы достать заказ, и вызывает у него метод из домена.

    // PayOrderHandler.php
    class PayOrderHandler {
        // Видишь? Не конкретная реализация, а интерфейс! Это ключ, ёпта!
        public function __construct(
            private OrderRepositoryInterface $repository
        ) {}
    
        public function handle(PayOrderCommand $command): void {
            // Достаём сущность из ядра
            $order = $this->repository->find($command->orderId);
            // Вызываем метод из ядра
            $order->markAsPaid();
            // Говорим репозиторию сохранить изменения
            $this->repository->save($order);
        }
    }
  3. Инфраструктурный слой (внешний, реализует интерфейсы приложения): А вот тут уже живёт вся грязь реального мира. Допустим, репозиторий на Doctrine. Он реализует тот самый OrderRepositoryInterface, который ждал хендлер. Он зависит от слоя приложения (от его интерфейса), а не наоборот!

    // DoctrineOrderRepository.php
    class DoctrineOrderRepository implements OrderRepositoryInterface {
        // А вот тут уже можно и EntityManager, это его дом
        public function __construct(private EntityManagerInterface $em) {}
    
        // Реализуем метод из интерфейса, используя Doctrine
        public function find(string $id): ?Order {
            return $this->em->find(Order::class, $id);
        }
    }

А нахуя это всё, спросишь? Да затем, чувак, чтобы не охуеть потом!

  • Домен независим. Его можно тестировать в полной изоляции, без поднятия базы и прочей ерунды. Просто создал объект и проверил логику. Красота.
  • Инфраструктуру — налево и направо. Надоел тебе Doctrine, который жрёт память, как не в себя? Хочешь на Eloquent переехать или вообще на чистый PDO? Без проблем! Пишешь новую реализацию OrderRepositoryInterface, меняешь строчку в конфиге контейнера — и всё, остальной код, включая всю бизнес-логику, даже не чихнул. Это и есть доверия ебать ноль к внешним штукам, и это правильно.
  • Всё на своих местах. Не будет у тебя в сущности Order метода saveToDatabase(). Это же пиздец, прости господи. Каждая сущность знает своё место.

В микросервисах это вообще архиважно. Один сервис можно переписать на другом фреймворке, поменять базу данных — и если ты не накосячил с архитектурой, то ядро, сама бизнес-логика, останется нетронутой. Иначе будешь потом, как полупидор, ползать по всему коду и выковыривать зависимости. Не доводи до этого, делай сразу по-человечески.