Что такое инфраструктурный слой (Infrastructure Layer) в чистой архитектуре?

Ответ

Инфраструктурный слой — это самый внешний слой в архитектуре приложения (например, в Clean Architecture или Hexagonal Architecture). Он содержит конкретные реализации абстракций, определённых во внутренних слоях (доменном и прикладном), и отвечает за взаимодействие с внешним миром.

Что находится в инфраструктурном слое:

  • Реализации репозиториев: Доступ к данным через конкретные технологии (MySQL через JDBC, MongoDB, REST API).
  • Внешние сервисы: Клиенты для email-рассылки, платежных шлюзов, message brokers (RabbitMQ, Kafka).
  • Фреймворки и драйверы: Веб-фреймворки (Spring MVC, JAX-RS), ORM (Hibernate), библиотеки для работы с файловой системой.
  • Конфигурация: Загрузка настроек из файлов, переменных окружения.

Ключевой принцип: Зависимости направлены вовнутрь. Инфраструктурный слой зависит от внутренних слоёв (домена и приложения), но не наоборот.

Пример на Java (Clean Architecture):

// 1. ДОМЕННЫЙ СЛОЙ (самый внутренний) - определяет абстракцию
package com.example.domain;
public interface UserRepository {
    User findById(UserId id);
    void save(User user);
}

// 2. ИНФРАСТРУКТУРНЫЙ СЛОЙ (самый внешний) - предоставляет реализацию
package com.example.infrastructure.persistence.jpa;
import com.example.domain.*;
import javax.persistence.EntityManager;

public class JpaUserRepository implements UserRepository {
    private final EntityManager em;
    // Зависимость на EntityManager (деталь инфраструктуры)
    public JpaUserRepository(EntityManager em) { this.em = em; }

    @Override
    public User findById(UserId id) {
        UserJpaEntity jpaEntity = em.find(UserJpaEntity.class, id.getValue());
        return jpaEntity != null ? jpaEntity.toDomain() : null;
    }

    @Override
    public void save(User user) {
        UserJpaEntity jpaEntity = UserJpaEntity.fromDomain(user);
        em.merge(jpaEntity); // Используем конкретный JPA API
    }
}

Таким образом, бизнес-логика (домен) ничего не знает о том, как именно хранятся данные — в MySQL, PostgreSQL или в файле. При необходимости можно заменить JpaUserRepository на InMemoryUserRepository для тестов или на MongoUserRepository без изменения кода домена.

Ответ 18+ 🔞

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

Что там внутри творится, в этой обслуге:

  • Репозитории на деле: Твои интерфейсы из домена тут обретают плоть. Хочешь в MySQL? Получи JdbcUserRepository. Захотелось в MongoDB? Вот тебе MongoUserRepository. Всё это конкретные работяги, которые знают, как юзать EntityManager или MongoTemplate.
  • Внешние контакты: Клиенты для отправки email, для общения с платёжкой, для шины сообщений (RabbitMQ, Kafka). Всё, что выходит за пределы твоего приложения.
  • Фреймворки и железо: Веб-контроллеры (Spring MVC, JAX-RS), ORM (Hibernate), работа с файлами. Всё, что является конкретной технологией, а не абстрактной бизнес-идеей.
  • Конфигурация: Чтение настроек из application.yml, переменных окружения. Тоже ведь общение с миром.

Главный закон, который нельзя нарушать: Зависимости идут строго внутрь. Инфраструктура зависит от домена и прикладного слоя, но никак не наоборот. Домен про них нихуя не знает и знать не должен. Это как шеф-повар — он даёт задание «принести говядину», но ему похуй, на каком автомобиле её привезли, главное, чтобы свежая была.

Смотри, как это выглядит в коде, на примере:

// 1. ДОМЕН (святая святых) - тут только правила и абстракции
package com.example.domain;
public interface UserRepository {
    User findById(UserId id);
    void save(User user);
}

// 2. ИНФРАСТРУКТУРА (рабочая лошадка) - тут вся грязь и конкретика
package com.example.infrastructure.persistence.jpa;
import com.example.domain.*; // Вот она, зависимость ВНУТРЬ!
import javax.persistence.EntityManager;

public class JpaUserRepository implements UserRepository {
    private final EntityManager em; // Конкретная железяка от Hibernate

    public JpaUserRepository(EntityManager em) { this.em = em; }

    @Override
    public User findById(UserId id) {
        // Превращаем доменный ID в то, что понимает JPA
        UserJpaEntity jpaEntity = em.find(UserJpaEntity.class, id.getValue());
        // А потом обратно в доменную сущность
        return jpaEntity != null ? jpaEntity.toDomain() : null;
    }

    @Override
    public void save(User user) {
        UserJpaEntity jpaEntity = UserJpaEntity.fromDomain(user);
        em.merge(jpaEntity); // А вот и конкретный вызов JPA, о котором домен не догадывается
    }
}

И вся фишка в чём? Если завтра начальство скажет «MySQL — говно, переезжаем на MongoDB», ты просто пишешь нового работягу — MongoUserRepository. А вся твоя бизнес-логика, все эти сложные алгоритмы, даже бровью не поведут, потому что они работают с интерфейсом UserRepository, а не с его конкретной реализацией. Для тестов вообще можно InMemoryUserRepository запилить. Красота, а не жизнь.