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

GRASP и паттерн ADR для веб-бэкенда

Разработчику Архитектору
Теория данных (раздел 3)

SOLID описывает свойства классов. GRASP (General Responsibility Assignment Software Patterns) отвечает на вопрос: кому поручить эту обязанность? — полезно при проектировании слоёв API, сервисов и домена.

Дополнение к паттернам проектирования и SOLID.


Ключевые принципы GRASP

ПринципВопросПример в бэкенде
Information ExpertКто владеет данными для решения?Order.calculateTotal() — не контроллер
CreatorКто создаёт B, зная A?OrderFactory создаёт OrderLine при известном Order
ControllerКто принимает системное событие?HTTP-контроллер, consumer очереди — не домен
Low CouplingКак уменьшить связность?Интерфейс PaymentGateway, не прямой SDK в домене
High CohesionЛогика одной темы вместе?BillingService, не "God service"
PolymorphismВариативность через типы?NotificationSender → email / push
IndirectionПосредник для гибкости?Репозиторий между сервисом и ORM
Protected VariationsСтабильный интерфейс при смене реализации?Адаптер внешнего API

GRASP не противоречит SOLID: Controller ↔ SRP на границе, Indirection ↔ DIP.


Где применять на практике

  • Controller тонкий — парсинг HTTP, код ответа, вызов одного use-case.
  • Expert в домене — скидки, статусы заказа, инварианты.
  • Creator не размазан: фабрики для сложных агрегатов.

Антипример — контроллер на 400 строк с SQL — нарушены Controller, Expert, Low Coupling.


ADR — Action–Domain–Responder

Эволюция MVC для HTTP (не путать с Architecture Decision Record — в контексте веба ниже именно Action–Domain–Responder).

РольОтветственность
ActionОдин вход: метод + путь → вызывает use-case
DomainБизнес-логика и сущности
ResponderФормирует HTTP-ответ (JSON, статус, заголовки)

Сравнение с классическим MVC:

MVCADR
Controller "толстеет"Action остаётся тонким
View для HTMLResponder для API (JSON)
Model смешивает персистентностьDomain отделён от инфраструктуры

Когда уместен ADR: JSON API, PHP (Aura, Slim-подобные), Node, где не нужен тяжёлый View.

Когда достаточно MVC + слоёв: Spring @RestController + @Service + repository — по сути те же роли, другие имена.


Связь с другими паттернами

  • Hexagonal / Ports-Adapters — Domain в центре, Action на driving side, Responder — адаптер вывода.
  • CQRS — разные Action/Responder для command и query.
  • Проектирование API — контракт Responder’а = OpenAPI-схема.

Чек-лист ревью

  1. Есть ли бизнес-правило в контроллере? → перенести в Domain (Expert).
  2. Создаёт ли контроллер десятки типов? → Creator / Factory.
  3. Меняется ли формат ответа независимо от логики? → Responder / DTO mapper.
  4. Тестируется ли домен без HTTP? → да, иначе связность высокая.

Связанные темы


Практический пример распределения ответственности

Сценарий: POST /orders.

  • Action принимает HTTP-запрос, валидирует формат, передает команду в use-case.
  • Domain проверяет инварианты заказа, резервирует товар, формирует событие.
  • Responder возвращает 201 Created и нормализованную структуру ответа.

Такой поток удерживает бизнес-логику вне HTTP-слоя и снижает связанность между веб-фреймворком и доменом.


Где часто ошибаются

  • Action содержит SQL и бизнес-правила одновременно.
  • Domain зависит от HTTP-кодов и формата JSON.
  • Responder начинает принимать решения домена вместо форматирования ответа.
  • Нет единого словаря ошибок и контракт "плывет" между эндпоинтами.

Эти ошибки усложняют тестирование и приводят к непредсказуемому поведению API.


Мини-чек-лист для code review

  1. Можно ли протестировать Domain без веб-сервера и БД?
  2. Зависит ли Domain только от абстракций, а не от инфраструктуры?
  3. Использует ли Action один use-case на одно системное действие?
  4. Формируется ли HTTP-ответ централизованно через Responder?
  5. Есть ли единый контракт ошибок для клиентов?