Перейти к содержимому

Слоистая архитектура ПО

Для учебной модели достаточно простой слоистой структуры. Она не требует углубления в DDD, Clean Architecture или конкретный backend-фреймворк, но помогает не смешивать ответственности.

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

Типовые слои:

  • presentation layer: взаимодействие с пользователем или внешним клиентом;
  • application layer: сценарии приложения и координация use cases;
  • domain/model layer: предметная модель и бизнес-правила;
  • infrastructure/data access layer: хранение, внешние сервисы, файлы, сетевые интеграции.

Главное правило слоистой модели: класс должен знать только о тех слоях, от которых он действительно зависит по ответственности. Пользовательский интерфейс или контроллер может обращаться к application layer. Application layer может использовать предметную модель и интерфейсы репозиториев или шлюзов. Infrastructure layer реализует технические детали хранения и интеграции. При этом предметная модель не должна напрямую зависеть от базы данных, HTTP-клиента, файловой системы или UI.

Типовые роли классов:

  • Controller принимает внешнее действие и передаёт его в application layer;
  • ApplicationService реализует сценарий приложения и координирует работу объектов;
  • DomainService содержит предметную операцию, если она не принадлежит одному объекту;
  • Repository скрывает способ получения и сохранения объектов;
  • DTO переносит данные через границу слоя или интерфейса;
  • Mapper преобразует одну техническую форму данных в другую;
  • Gateway или Adapter изолирует внешнюю систему;
  • Entity или Model представляет предметный объект в технической модели.

Эти роли не являются обязательным набором для любой системы. Их нужно добавлять только тогда, когда они помогают реализовать выбранные сценарии и удержать зависимости между слоями.

Например, в сценарии Оформить заказ контроллер может принять команду от клиента и передать её в OrderApplicationService. Сервис приложения загружает нужные данные через OrderRepository, вызывает операции предметных объектов, проверяет правила и сохраняет результат. Если нужно обратиться к платёжной системе, application layer работает не напрямую с конкретным API, а через PaymentGateway или похожий интерфейс. Конкретный адаптер платёжной системы относится к infrastructure layer.

Такой пример показывает, что слои не равны папкам в проекте. Папки помогают организовать код, но архитектурный смысл появляется только тогда, когда зависимости между классами соответствуют выбранным ролям. Если контроллер сам проверяет бизнес-правила, формирует SQL-запросы и вызывает внешний сервис, слоистая архитектура фактически отсутствует, даже если файлы лежат в разных каталогах.

Типичные ошибки слоистой модели:

  • помещать бизнес-правила в контроллеры или DTO;
  • делать repository ответственным за сценарий целиком;
  • заставлять domain/model layer знать о таблицах, JSON или HTTP;
  • добавлять сервисы без реальной ответственности;
  • проводить зависимости между пакетами в обе стороны;
  • называть классы по слоям, но не менять их фактическое поведение.

Для UML это важно потому, что техническая диаграмма классов и диаграмма пакетов должны показывать не просто набор классов, а выбранное распределение ответственности. Если зависимости между слоями не видны, модель плохо объясняет реализацию сценария.