Как стать автором
Обновить

GRASP: почему настоящая архитектура начинается не с SOLID

Уровень сложностиСредний
Время на прочтение12 мин
Количество просмотров32K

Многие инженеры начинают своё архитектурное мышление с SOLID. Затем изучают GoF-паттерны. Но всё равно остаётся ощущение, что чего-то не хватает: распределения ответственности, логики построения модели, связи структуры и поведения. И вот тут GRASP — это как раз то недостающее звено.

Хочу начать с личной предыстории.

Давным-давно, как и многие из вас, я читал умные книжки: «Чистый код» и «Чистая архитектура» Роберта Мартина, «Совершенный код» Стива Макконнелла и другие.

Также не обошли меня и классические принципы проектирования — SOLID, KISS, DRY — и, думаю, каждый читатель добавит сюда свои.

Безусловно, это всё важные и фундаментальные вещи.

Но однажды на горизонте появилось DDD — предметно-ориентированное проектирование в изложении Эрика Эванса. Именно его «синяя книга» стала культовой и задала язык для архитектурного мышления.

Позже я открыл и «красную книгу» Вона Вернона, где DDD уже рассматривался с точки зрения практической имплементации: архитектура, код, реальные подходы в проектах.

Читая Эванса, рассматривая его диаграммы классов и примеры кода, я всё думал: как он это делает?

Самым большим открытием для меня стало то, что книга DDD хоть и показывает стратегические и тактические приёмы — агрегаты, объекты‑значения, спецификации, фабрики и т. д. — но не учит проектировать саму предметную область.

Складывалось ощущение, что мы это уже откуда‑то должны были знать.
А откуда — остаётся загадкой.

Эванс очень подробно рассказывает:

  • о принципах предметно‑ориентированного мышления,

  • о тактических паттернах — агрегатах, сущностях, объектах‑значениях, репозиториях,

  • о стратегических подходах — ограниченных контекстах, контекстных картах, ubiquitous language.

Но он почти не даёт методики: как из требований, сценариев, реальной жизни — прийти к модели. Он как бы подразумевает, что ты уже нащупал нужные сущности — и теперь просто нужно их правильно оформить.

А как ты их нащупал — остаётся за кадром.

При этом стоит отметить, что и SOLID‑принципы не дают внятных ответов на вопрос:
как именно проектировать предметную область? Как и многие другие — безусловно великие — практики и труды.

Со временем начинаешь приходить к одной странной мысли:
все говорят про ООП, все пишут на объектно‑ориентированных языках — но писать и понимать, что ты пишешь, оказывается не одно и то же.

И со временем приходит странное ощущение. Будто вся архитектурная зрелость современного инженера сводится к SOLID, Чистому коду, и, максимум, — паттернам GoF.

То есть — к правилам оформления, к «как писать»,
но не к «что моделировать» и «почему именно так».

SOLID, безусловно, важен. Он помогает писать поддерживаемый код, говорит о структуре классов, об устойчивости к изменениям. Но он почти не затрагивает смысловую часть архитектуры — то, как должна выглядеть модель предметной области, какие объекты мы проектируем, где границы, откуда берутся поведения и ответственности.

И в этом кроется одна из главных ловушек:

Но потом сталкиваешься с реальной сложной системой — и вдруг осознаешь: тех инструментов, которые ты знал, просто не хватает. Они говорят о коде — но не о модели. О принципах оформления — но не о сути конструкции.

Хочется привести аналогию. Из личного опыта — сноуборд.

Многие учатся кататься сами. Смотрят видео, повторяют за другими, вроде бы все получается. Но потом приходят к тренеру — и вдруг оказывается:

  • стойка неправильная

  • корпус работает против доски

  • закантовка срывается

  • вес распределяется хаотично

Вседержится «на чувстве» и годах практики, но нет системы, нет понимания, нет архитектуры движения. Можно ехать. Но ты не знаешь — почему именно так.

То же самое и с ООП.
Да, ты используешь классы и объекты.
Да, есть интерфейсы, инъекции, модули.

Но если ты просто создаешь Hibernate-сущности с геттерами и сеттерами и меняешь их состояние в сервисах — это еще не объектно-ориентированное проектирование.

Это код на ООП-языке. Но не ООП как мышление.


Когда все встало на места: Крэг Ларман и его «Применение UML и шаблонов проектирования»

Следующей важной вехой для меня стала книга, которая произвела неизгладимое впечатление — «Применение UML 2.0 и шаблонов проектирования» автора Крэга Лармана
(в оригинале — «Applying UML and Patterns: An Introduction to Object‑Oriented Analysis and Design and Iterative Development»).

После нее мне показалось, что я наконец нашел ответы почти на все вопросы, которые накопились за годы практики.

Ларман рассматривает весь процесс проектирования ПО:
от анализа требований — до проектирования, программной реализации и даже тестирования.
В книге — четкая методика, живая логика и настоящий процесс мышления архитектора.

В какой-то момент мне даже показалось, что Ларман говорит о DDD больше, чем сам Эванс.

Он начинает с текста требований и use-case'ов,
анализирует сценарии — и на их основе формирует концептуальную модель.

Но концептуальная модель — это еще не классы и не код.
Это — модель предметной области, построенная на уровне бизнес-понятий.

Тем не менее, уже на этом этапе выявлены:

  • ключевые бизнес-сущности,

  • связи между ними,

  • важные атрибуты и поведения.

Цельэтого этапа — понять, какие концепции вообще существуют в системе —
еще до написания первой строчки кода.

Интересно, что книга Лармана вышла еще в 1997 году — то есть до появления DDD Эрика Эванса (первая публикация — 2003).

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

  • Ларман дает процесс — от анализа требований до концептуального проектирования.
    Он показывает, как из пользовательских сценариев прийти к модели, выявить сущности, роли и поведение.

  • Эванс, в свою очередь, формализует язык и паттерны для работы с предметной областью.
    Он показывает тактические конструкции (агрегаты, фабрики, спецификации)
    и стратегические приёмы — ограниченные контексты, контекстные карты, Ubiquitous Language.

DDD не столько про «как найти модель», сколько про то, как с ней обращаться,
и какие принципы соблюдать, когда модель уже начала формироваться.

Поэтому Лармана вполне можно воспринимать как учебник архитектурного анализа,
а DDD — как специализированное продолжение, фокусирующееся на глубине домена и границах модели.

«Люди часто спрашивают меня о том, с какой книги лучше всего начать знакомство с объектно‑ориентированным проектированием. С тех пор, как я увидел книгу „Применение UML и шаблонов проектирования“, я рекомендую именно её.»
Мартин Фаулер

Когда концептуальная модель уже сформирована — наступает следующий шаг:
выделение ролей объектов, ответственности и поведения.

И вот тут в игру вступает GRASP.

Всего представлено девять шаблонов:

  • Information Expert

  • Creator

  • Controller

  • Low Coupling

  • High Cohesion

  • Polymorphism

  • Pure Fabrication

  • Indirection

  • Protected Variations

Группа 1. Фундаментальные принципы модели

(Cohesion, Coupling, Information Expert, Creator)

Эти паттерны задают самую суть: структуру, связанность и здравый смысл проектирования. Они применимы и к отдельным классам, и к модулям, и к сервисам — формируя основу, на которую опираются остальные архитектурные решения.

Cohesion и Coupling — “первое дыхание архитектуры”

  • High Cohesion — высокая связанность обязанностей внутри компонента.
    Когда поведение логически объединено и сфокусировано, система становится:

    • проще в поддержке,

    • легче для понимания,

    • надёжнее при изменениях.

  • Low Coupling — низкая связанность между компонентами.
    Чем меньше зависимостей между модулями, тем:

    • выше модульность,

    • меньше каскадных изменений,

    • легче тестировать и заменять части системы.

Эти два принципа работают не только на уровне классов, но и на уровне архитектурных границ.
Например, в DDD Bounded Context — это своего рода макро-Cohesion:
внутри контекста — единая, связанная модель,
снаружи — слабосвязанные интерфейсы между контекстами.

Information Expert — поведение рядом с данными

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

  • Это позволяет повысить Cohesion,

  • снизить дублирование,

  • избавиться от анемичных моделей,

  • и перейти к rich-домену, где данные и поведение неразделимы.

Information Expert — один из самых сильных паттернов GRASP, потому что он помогает не «придумывать архитектуру», а выводить ее из самой модели.

Creator — тот, кто ближе к объекту, и должен его создавать

Если некий объект B:

  • содержит объект A,

  • записывает его,

  • активно использует,

  • или обладает нужными данными для инициализации A —
    то именно B должен быть ответственен за создание A.

В каком-то смысле, можно упрощённо сказать, что Creator — это частный случай Information Expert, применимый именно к созданию объектов. Он следует той же логике: «у кого есть данные — тот и действует», но сфокусирован на одном конкретном аспекте — моменте рождения.

Такой подход помогает:

  • снизить Coupling,

  • локализовать ответственность,

  • и не выносить создание объектов в искусственные фабрики, если это не нужно.

Группа 2. Инженерные надстройки

(Pure Fabrication, Indirection, Controller)

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

Pure Fabrication — свобода инженерного мышления

Иногда, следуя паттернам вроде Information Expert, можно слишком буквально перенести ответственность в бизнес-объект. Например, в Active Record бизнес-сущность знает, как себя сохранить в базу данных.

Но это нарушает SRP и приводит к сильной связанности с инфраструктурой.

В рамках чистой архитектуры и DDD такой подход считается антипаттерном.

Pure Fabrication предлагает другой путь: создавать искусственные абстракции, которые:

  • не отражают предметную область,

  • но нужны для архитектурной чистоты,

  • и соблюдают принципы Low Coupling и High Cohesion.

Классический пример — репозиторий. Это не бизнес-сущность, а прослойка между логикой и базой данных.

На практике любой класс, созданный ради читаемости, структурности, тестируемости — это проявление Pure Fabrication.

Еще один пример из книги Лармана — объект Cup, который отвечает за выбрасывание кубиков и подсчет суммы.

Такой сущности нет в бизнесе, но она помогает изолировать поведение и улучшить структуру.

Indirection — управляй зависимостями через прослойки

Indirection (перенаправление) — это паттерн, снижающий жесткую связанность компонентов. Он создаёт контролируемую прослойку между объектами и дает системе:

  • управляемость,

  • гибкость,

  • возможность замены компонентов без каскадных правок.

По сути, Indirection — это идея, лежащая в основе множества архитектурных решений.

  • В DDD это может быть сервис, координирующий бизнес-логику.

  • В гексагональной архитектуре — адаптер, получающий данные извне.

  • В UI — контроллер, принимающий пользовательский запрос и передающий дальше.

Indirection — это не просто «прокинуть вызов». Это может быть и обогащение данных, и трансформация интерфейса, и дополнительная логика, в зависимости от нужд системы.

Поэтому такие паттерны, как Adapter, Decorator, Proxy, Facade — можно рассматривать как частные реализации идеи Indirection:

Паттерн

Что делает

Как проявляется Indirection

Proxy

Перехватывает вызовы

Промежуточный объект между клиентом и реализацией

Decorator

Расширяет поведение без изменения кода

Оборачивает объект с дополнительной логикой

Adapter

Преобразует интерфейс под ожидаемый формат

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

Facade

Упрощает доступ к сложной подсистеме

Интермедиар между клиентом и множеством компонентов

Repository

Абстрагирует доступ к данным

Посредник между бизнес-логикой и инфраструктурой

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

Indirection + Pure Fabrication: пересечение смыслов

Интересно, что некоторые архитектурные элементы — например, репозиторий — могут одновременно быть проявлением нескольких GRASP-паттернов.

  • С одной стороны, репозиторий — это Pure Fabrication, потому что в предметной области нет такого понятия. Это искусственная абстракция, созданная ради структурной чистоты и соблюдения принципов модульности.

  • С другой — репозиторий реализует принцип Indirection, так как служит прослойкой между бизнес-логикой и слоем хранения, снижая связанность и повышая гибкость.

Это отличный пример того, как GRASP-паттерны не исключают, а дополняют друг друга, проявляясь на разных уровнях архитектуры. Именно в этом — сила зрелого объектного проектирования.

Controller — паттерн первого звена координации

GRASP-паттерн Controller отвечает на вопрос:

Кто первым получает внешние события и координирует поведение системы?

Это может быть любой компонент, стоящий на границе системы: HTTP-контроллер, gRPC endpoint, CLI-команда, Kafka-консьюмер. Он принимает входные данные, определяет сценарий и делегирует выполнение вглубь архитектуры. Именно поэтому Controller называют паттерном первого звена координации.

В книге Лармана Controller может представлять либо всю систему, либо один конкретный сценарий — use case. Во втором случае он сближается с идеей use-case-класса или application-сервиса, который инкапсулирует логику одного бизнес-процесса.

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

Где проявляется Controller на практике

  • RestController, CommandHandler

  • gRPC endpoint или API gateway

  • Kafka-консьюмер, listener RabbitMQ

  • CLI-команда

  • Scheduler, запускающий периодическую логику

  • UseCase/Service-класс, реализующий сценарий

Контроллеры на разных уровнях

Controller может жить на разных слоях:

  • UI-контроллер — принимает вход пользователя, парсит, валидирует

  • Use-case-контроллер — координирует сценарий, вызывает нужные сервисы

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

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

Controller — точка входа в систему. Именно здесь можно:

  • обернуть выполнение в транзакцию,

  • проверить права доступа,

  • залогировать сценарий или метрики,

  • адаптировать входные данные к внутреннему API.

Хорошо спроектированный Controller делает систему прозрачной и предсказуемой.
Плохо спроектированный — превращает вход в кашу из проверок, вызовов и бизнес-логики.

Группа 3: Polymorphism и Protected Variations — про гибкость, устойчивость и защиту от изменений

Polymorphism (Полиморфизм)

Полиморфизм — одна из базовых концепций ООП. Согласно определению, это способность функции работать с объектами разных типов.

Различают несколько форм:

  1. Параметрический полиморфизм
    В Java — это дженерики: List<T>, Optional<T>. Код обобщён, и не зависит от конкретного типа.

  2. Ad-hoc полиморфизм (перегрузка)
    Одинаковое имя метода с разными параметрами:

    void print(String s) { ... }
    void print(int i) { ... }
  3. Subtype Polymorphism (подстановочный полиморфизм)
    Классический пример: наследование, интерфейсы, переопределение методов.

    Shape shape = new Circle();
    shape.draw(); // Вызовется реализация из Circle

Паттерн Polymorphism из GRASP отвечает на вопрос:

Как проектировать поведение, которое должно различаться в зависимости от типа объекта?

Решение: Вынеси поведение в абстракцию — и пусть каждый тип реализует свое.

Пример без полиморфизма:

if (shape instanceof Circle) {  
  ((Circle) shape).draw();
} else if (shape instanceof Rectangle) {  
  ((Rectangle) shape).draw();
}

Пример с полиморфизмом:

interface Shape { void draw(); }

class Circle implements Shape {  
  public void draw() { ... }
}

Преимущества:

  • убираешь условную логику (if, switch);

  • код становится расширяемым без изменений в старом;

  • достигаешь принципа OCP — open for extension, closed for modification.

Protected Variations (Защита от изменений)

Если Polymorphism — это механизм, то Protected Variationsархитектурная цель.

Как защитить компоненты системы от изменений в других частях?

Суть:

Изолируй потенциально изменяемые участки стабильными абстракциями.
Предусмотри вариативность заранее — как архитектурную страховку.

Область

Пример

DDD

Репозиторий защищает доменную модель от изменений в БД

Hexagonal Architecture

Адаптеры защищают ядро от API, UI, брокеров

Чистая архитектура

Внешние слои зависят от внутренних через интерфейсы

Protected Variations не про классы — а про архитектурное мышление:
— Где система нестабильна?
— Где точка потенциальной замены?
— Как изолировать важное от хрупкого?

Связь с принципами SOLID

Также необходимо упомянуть и принцип подстановки Барбары Лисков (LSP):

«Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом».

Абсолютно верно, Википедия дает здесь отсылку к контрактному программированию.
Своего рода LSP вводит поведенческую гарантию: если вы проектируете абстракцию — реализуйте ее корректно. Иначе подстановка просто ломает систему.

Многие интуитивно связывают LSP с полиморфизмом: раз уж подставляем типы — нужно делать это правильно.

Но если смотреть глубже, принцип Лисков ближе по духу к GRASP: Protected Variations.
Это про то, как гарантировать стабильность и безопасность при замене компонентов через абстракции. LSP отвечает за то, чтобы вариативные точки действительно были защищены — поведенчески, а не только структурно.

В свою очередь, принцип инверсии зависимостей (DIP) говорит:

«Высокоуровневый код не должен зависеть от деталей.»

То есть — абстрагируйся от нестабильного. Ровно то, о чём говорит Protected Variations в GRASP.

OCP (Open/Closed Principle) — еще один ключевой элемент, логически продолжающий связку. Он как раз перекликается с Polymorphism и Protected Variations, но с акцентом на изменяемость через расширение.

Это тот принцип, который начинающие разработчики часто не понимают:

“Программные сущности должны быть открыты для расширения, но закрыты для модификации.”

Как так?

Вся суть — в выделении правильных абстракций.

Это напрямую связано с:

  • Polymorphism — расширяемость через подстановку реализаций;

  • DIP — изоляция через абстракции, чтобы расширения не трогали клиентов;

  • LSP — поведенческая гарантия, что новые реализации не ломают старое;

  • Protected Variations — архитектурные буферы, чтобы изменения не «пробивали» клиентов.

OCP — это не средство, а цель.
Все остальные — это способы еt достичь.

Как GRASP-паттерны влияют на Cohesion и Coupling

Паттерн

Влияние на Cohesion и Coupling

Information Expert

Повышает Cohesion — поведение рядом с данными

Creator

Снижает Coupling — создание у ближайшего соседа

Pure Fabrication

Повышает Cohesion — выделение искусственного, но логичного объекта

Indirection

Снижает Coupling — прослойка между компонентами

Controller

Снижает Coupling UI и бизнес-логика, повышает Cohesion на входе

Polymorphism

Снижает Coupling, повышает расширяемость и заменяемость

Protected Variations

Локализует Coupling, создает буферы для изменений

Вместо вывода

Многие инженеры начинают с практик, паттернов, фреймворков. Но по-настоящему зрелый подход начинается с понимания принципов. Не с SOLID, не с GoF, не с модных архитектурных терминов — а с глубинной логики распределения ответственности, устойчивости и смысла конструкции.

Именно это дает GRASP.

“A man who grasps principles can successfully select his own methods.
A man who tries methods, ignoring principles, is sure to have trouble.”
 — Ralph Waldo Emerson

«Тот, кто понимает принципы, сам найдет нужные методы. А тот, кто пробует методы, игнорируя принципы, неминуемо столкнется с проблемами.»

GRASP — это не просто девять паттернов. Это попытка выразить то, как мы должны думать о системе — на уровне архитектурной интуиции. Чтобы потом уже выбирать подходящие инструменты, архитектурные формы и даже стили программирования.

Зная принцип — ты всегда сможешь вывести практику. Но зная только практику — легко потеряться, когда реальность выходит за рамки учебника.


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

CTO / архитектор для старта или перезапуска проекта — пишите: @korobovn, мой канал t.me/cto_way, korobovn@gmail.com

Теги:
Хабы:
+89
Комментарии57

Публикации

Истории

Работа

Ближайшие события

19 марта – 28 апреля
Экспедиция «Рэйдикс»
Нижний НовгородЕкатеринбургНовосибирскВладивостокИжевскКазаньТюменьУфаИркутскЧелябинскСамараХабаровскКрасноярскОмск
24 апреля
VK Go Meetup 2025
Санкт-ПетербургОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань
14 мая
LinkMeetup
Москва
5 июня
Конференция TechRec AI&HR 2025
МоскваОнлайн
20 – 22 июня
Летняя айти-тусовка Summer Merge
Ульяновская область