open atlas
↑ К треку
Архитектурные паттерны ARCH · 03 · 01

N-уровневая слоистая архитектура

Слоистая архитектура делит код на горизонтальные уровни — presentation, application, domain, infrastructure. Каждый слой зависит только от нижележащего. Даёт замещаемость и тестируемость, но порождает sinkhole, когда слои просто проксируют данные.

ARCH Middle ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

B2B-платформа заказов начиналась как один файл — обработчик запросов, который читал из базы данных, выполнял валидацию и возвращал ответ. За шесть месяцев файл разросся до 3000 строк. Команда разбила его на «слои»: пакет контроллеров, пакет сервисов, пакет репозиториев. Слоистость сделала код навигируемым. Можно было писать тесты против сервисов без живой базы данных. Когда команда перешла с REST-клиента к нативному драйверу — изменился только пакет репозиториев. Через два года платформа имела двенадцать классов-сервисов. Каждый метод сервиса следовал одному и тому же паттерну: валидировать вход, вызвать репозиторий, вернуть результат. Ни один метод ничего не делал, кроме делегирования — вызывал один метод репозитория и возвращал то, что тот возвращал, без изменений. Каждое изменение требовало затронуть контроллер, сервис и репозиторий в связке. Слоистость, которая должна была давать замещаемость, превратилась в церемонию: три файла на каждое однострочное бизнес-правило. Команда правильно назвала слои, но так и не спросила, что каждый слой должен добавлять.

Четыре стандартных слоя

Каноническая n-tier-модель описывает четыре слоя, каждый со своей ответственностью:

Presentation layer — обрабатывает всё, что относится к пользовательскому интерфейсу: парсинг HTTP-запросов, десериализация входных данных, форматирование ответов, аутентификационные токены, согласование контента. Знает о веб-протоколе. Не знает о бизнес-правилах.

Application layer — оркестрирует use case’ы. Последовательно вызывает domain и infrastructure, управляет транзакциями, применяет политику авторизации, публикует события. Знает о потоке use case’а. Не содержит бизнес-логику — делегирует в domain.

Domain layer — содержит бизнес-модель: сущности, value objects, доменные сервисы и правила, управляющие ими. На B2B-платформе «заказ нельзя подтвердить, если у него есть просроченные счета» — это доменная логика. У этого слоя нет знания о базе данных, HTTP-фреймворке или системе очередей.

Infrastructure layer — реализует порты, которые определяют domain и application: репозитории баз данных, отправители email, публикаторы очередей, клиенты внешних API. Знает о технологиях. Не знает о бизнес-правилах.

Strict vs relaxed layering

Strict layering требует, чтобы каждый слой зависел только от непосредственно нижележащего. Presentation вызывает Application; Application вызывает Domain; Domain определяет интерфейсы, которые реализует Infrastructure. Domain не имеет никакого знания о Infrastructure — не импортирует драйвер БД, ORM или классы фреймворка.

Strict layering даёт наиболее сильную изоляцию: domain можно тестировать без базы данных, очередей, внешних сервисов. Его можно понять без знания какой-либо технологии. Это фундамент, на котором строятся hexagonal и clean architecture (эти паттерны — strict layering, доведённый до логического завершения, о них — в unit’ах 04 и 05).

Relaxed layering (также «open layer» architecture) позволяет слою вызывать любой нижележащий слой, не только непосредственно смежный. Presentation может вызвать Domain напрямую, минуя Application. Application может импортировать классы Infrastructure напрямую, а не через интерфейс.

Relaxed layering прагматичен: для простых CRUD-операций application layer не добавляет ценности, и принудительный вызов через него — церемония. Компромисс: relaxed layering легче нарушить незаметно — как только разрешено пропускать слои для удобства, дисциплина размывается, и в итоге presentation импортирует классы репозиториев напрямую.

Почему это работает

Зачем вообще нужна слоистость? Исходная сила, которую она адресует, — замещаемость. Когда B2B-платформа переходила на другой драйвер базы данных, изменился только слой infrastructure — потому что никакая бизнес-логика не утекла в него. Когда команде потребовалось тестировать правила подтверждения заказа изолированно — это было возможно, потому что domain layer не зависел от базы данных. Оба преимущества требуют, чтобы границы слоёв держались: чтобы ни один вышележащий слой не импортировал детали реализации нижележащего. В момент, когда бизнес-логика утекает в репозиторий или логика presentation — в доменную сущность, эти преимущества исчезают.

Что слоистость реально даёт — и какова её стоимость

Конкретные выгоды хорошо поддерживаемой слоистой архитектуры:

Замещаемость — infrastructure можно заменить (новая БД, новый message broker, новый email-провайдер) без изменения domain. Работает только тогда, когда domain layer определяет интерфейс, а infrastructure реализует его.

Независимая тестируемость — domain можно тестировать с чистыми in-memory двойниками. Application layer — с fake infrastructure. Для проверки бизнес-правила не нужен end-to-end тест.

Навигируемость — инженеры знают, где искать каждый тип изменений. Изменение URL-роута: Presentation. Изменение потока use case’а: Application. Изменение бизнес-правила: Domain. Изменение SQL: Infrastructure.

Стоимость: каждое сквозное изменение (добавить поле в сущность, выставить его в API, сохранить в БД) требует затронуть четыре файла в четырёх пакетах. Слоистая архитектура обменивает горизонтальную модульность на вертикальную жёсткость. Добавление функциональности — вертикальный разрез через все слои; архитектура делает этот разрез явным и тем самым несколько утомительным.

Антипаттерн architecture sinkhole

Architecture sinkhole — это сбой, описанный во вступительной истории: слой, не добавляющий ценности и просто проксирующий вызовы в нижележащий слой.

// Сервис — чистый sinkhole — не добавляет ничего
class OrderService {
  async getOrder(id: string) {
    return this.orderRepository.findById(id);  // нулевое преобразование, нулевая логика
  }
  async createOrder(dto: CreateOrderDto) {
    return this.orderRepository.save(dto);  // нулевая валидация, нулевая доменная логика
  }
}

Sinkhole-сервис не просто бесполезен — он вреден. Он добавляет косвенность без ценности. Каждое изменение затрагивает лишний файл. Он приучает команду рефлекторно добавлять методы в сервис, даже когда тому нечего привнести.

Sinkhole — не признак того, что слоистость была ошибкой. Это признак того, что domain layer выхолощен. Если он не содержит реальной логики, application layer нечего оркестрировать, и сервисный слой становится прокси. Исправление — не убрать слои, а вернуть логику туда, где ей место: в domain (урок 03-02 разбирает это подробно как паттерн anemic-domain-model).

lesson.inset.note

Марк Ричардс и Нил Форд рекомендуют «правило 80/20» для sinkhole: если более 20% запросов через слой просто проходят насквозь без преобразования, архитектура накапливает sinkhole-долг. Это не точная метрика, а диагностика — если вы регулярно пишете пустые методы делегирования, слоевая структура не окупается.

Викторина

На B2B-платформе заказов добавляется новое правило: заказы стоимостью более $10 000 должны быть одобрены старшим менеджером перед подтверждением. Куда относится эта логика в четырёхслойной архитектуре и почему?

Викторина

Команда использует strict layering. Инженер предлагает, чтобы domain layer напрямую импортировал библиотеку логирования для записи нарушений инвариантов. Что это нарушает и каков правильный подход?

Викторина

У команды есть OrderService с 50 методами, где 40 методов просто вызывают соответствующий метод OrderRepository и возвращают результат без изменений. Какую архитектурную проблему это описывает и какова её первопричина?

Вспомните перед уходом
  1. 01
    Каковы четыре стандартных слоя n-tier архитектуры и конкретная ответственность каждого?
  2. 02
    В чём разница между strict и relaxed layering и каков трейдофф?
  3. 03
    Что такое антипаттерн architecture sinkhole и какова его первопричина?
Итог

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

Четыре слоя — presentation, application, domain, infrastructure — каждый владеет отдельной ответственностью. Правило зависимостей внутри слоистой архитектуры: каждый слой зависит только вниз. Presentation вызывает Application, Application вызывает Domain, Domain определяет интерфейсы, которые Infrastructure реализует. Ни один слой не импортирует из вышележащего.

Strict layering обязывает каждый слой зависеть только от непосредственно нижележащего. Relaxed layering допускает пропуск слоёв в прагматичных случаях. Оба варианта валидны; strict layering даёт более сильную изоляцию ценой большей дисциплины.

Architecture sinkhole — это сбой: слой, проксирующий вызовы без добавленной ценности. Sinkhole’ы появляются, когда domain layer пуст — когда бизнес-правила ушли в репозиторий или просто не были зафиксированы в доменных объектах. Исправление — не убрать слои, а восстановить ответственность domain layer. Следующий урок разбирает, что это означает: разницу между anemic domain model (пакеты данных + логика в сервисах) и rich domain model (поведение вместе с данными, которые оно охраняет).

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.