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

Принцип обратных зависимостей

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

Диаграмма иллюстрирует фундаментальный принцип обратных зависимостей в архитектуре программного обеспечения. Она демонстрирует разделение системы на два основных слоя:

Стабильное Ядро содержит абстракции — интерфейсы и контракты, которые определяют «что должно быть сделано», но не конкретные реализации. Эти абстракции остаются неизменными при смене технологий или реализаций.

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

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

Практика: вносим изменения

Давайте рассмотрим сценарий, в котором требуется обновить Реализацию A1 с версии v1 на v2. Это может быть все, что угодно – метод оплаты, другая схема сбора персональных данных, иной графический интерфейс или что-то еще.

На схеме вы видите, ка происходит замена с учетом принципа обратных зависимостей. Мы локализуем изменения при модернизации системы и минимизируем воздействие на остальную часть ИТ-ландшафта. Итого здесь можно выделить 4 преимущества более гибкого подхода:

·        Устаревшая реализация помечена на удаление, но при этом не требует изменений в стабильном ядре.

·        Новая реализация добавляется в систему, реализуя тот же стабильный контракт (Абстракция 1).

·        Стабильное Ядро остается полностью неизменным, все абстракции продолжают работать без модификаций.

·        Остальные реализации (C, D, E) также не затрагиваются изменениями и продолжают функционировать в обычном режиме.

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

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

Глубокая интеграция с предметной областью

Предметная область (domain) — это не просто набор функций или требований, а сложная экосистема бизнес-процессов, правил и ограничений, которые определяют реальную ценность программной системы для конечных пользователей.  Но особенность бизнес-логики заключается в ее врожденной нестабильности. Рыночные условия трансформируются, законодательные нормы обновляются, бизнес-модели эволюционируют — и каждое такое изменение, если к нему не подготовиться, неизбежно достигнет ядра системы и создаст проблемы.

Эта реальная волатильность делает продуманные архитектурные решения критически важными для долгосрочного успеха проекта. И здесь практически невозможно обойтись без применения Domain-Driven Design. DDD предоставляет концептуальный каркас для глубокого понимания предметной области через:

·        Ubiquitous Language — единый язык общения между разработчиками и экспертами предметной области

·        Bounded Contexts — четкие границы ответственности внутри системы

·        Aggregates — инкапсуляция бизнес-инвариантов

Декомпозиция на основе нестабильности дополняет этот концептуальный аппарат практическим инструментарием для изоляции волатильных бизнес-правил в независимые, слабосвязанные компоненты. Вместе декомпозиция на основе нестабильности и DDD образуют мощный симбиоз. Мы фактически получаем архитектурный иммунитет к бизнес-изменениям.

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

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

1.     Domain Layer — содержит только бизнес-сущности, правила и интерфейсы репозиториев

2.     Application Layer — оркестрирует бизнес-процессы, но не содержит бизнес-логики

3.     Infrastructure Layer — реализует технические детали: базы данных, внешние API, messaging

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

Четыре шага к архитектурной устойчивости

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

Стремясь к архитектурной устойчивости, мы как раз начинаем применять практику проектирования на основе нестабильности — то есть разделяем систему не по функциям, а по вероятности изменений. Вместо вопроса "что система делает?" мы задаем вопрос "что в системе может измениться?". А чтобы эта работа велась системно, необходимо выявить оси нестабильности

Поиск осей нестабильности

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

Можно выделить три типа осей нестабильности:

Технологические – внешние API и интеграции, базы данных, UI/UX фреймворки, протоколы коммуникации.

Бизнесовые – способы оплаты, правила ценообразования, методы аутентификации, отчетность.

Операционные – мониторинг и логирование, механизмы кеширования, процедуры развертывания.

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

Шаг 1: Анализ истории изменений

·        Изучите, какие модули менялись чаще всего.

·        Определите «точки боли» системы, где команда тратила больше всего времени.

Шаг 2: Создание стабильных абстракций

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

Шаг 3: Изоляция нестабильных компонентов

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

Шаг 4: Сборка через композицию

·        Стабильное ядро системы собирает различные реализации через Dependency Injection, опираясь на абстракции, а не на конкретные реализации.

Заключение: от сопротивления к работе с изменениями

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

Функциональная декомпозиция заставляет команды выживать — постоянно бороться с последствиями изменений, тратить непропорциональные усилия на простые задачи и постоянно рисковать срывом сроков. Декомпозиция на основе нестабильности не предотвращает изменения требований, но она радикально снижает стоимость этих изменений.

убедился на опыте многих проектов, что не стоит проектировать ПО под конкретные требования! Конечно же требования должны быть реализованы, но зачастую требуется сделать несколько больше чем напечатать в консоли "Hello World", например сделать метод, который позволяет выводить в консоль все что его попросят (иногда стоит вывести просто "Hello World").  Лучше создавать системы, готовые к изменениям, которые неизбежно произойдут. Архитектурная устойчивость — это не роскошь, а необходимое условие для разработки и развития программного обеспечения, способного поддерживать бизнес в долгосрочной перспективе.