Эволюционная архитектура на практике

Alexander Polomodov
9 min readNov 3, 2021

--

По мере развития систем часто возникает момент, когда для дальнейшего развития требуется вложить много сил в рефакторинг процессов работы команд и архитектуры системы. Я проходил через такое не один раз и собрал набор симптомов, корневых причин проблем и работающих методов их решения. В итоге, я решил рассказать про подходы к эволюционной архитектуре на Tinkoff Agile Conference 2021, которая прошла в октябре 2021 года. Так как аудитория конференции была в основном менеджерской, то глубоких технических деталей в докладе нет.

Рис. “Титульный слайд выступления”

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

Вид.1 “Запись выступления”

Начать стоит с того, а зачем нам эволюционная архитектура в Tinkoff. Причин к этому несколько и все они изложены ниже, но если кратко, то у нас растет количество клиентов, продуктов, и интеграций продуктов между собой. Чтобы успевать за этим мы постоянно расширяем команды и часто крупные монолитные команды делим на stream-aligned команды, вдоль приоритетных продуктов. Это приводит к потребностям в изменениях в архитектуре систем.

Рис. “К чему нам в Tinkoff эволюционная архитектура”

Следующим пунктом идет вопрос, а чем эволюционная архитектура может помочь читателям статьи. Основные моменты отмечены ниже.

Рис. “Зачем эволюционная архитектура читателям”

А теперь, когда мы поняли какой это полезный зверь “эволюционная архитектура”, кажется пришла пора понять а что это такое:)

Рис. “А что такое архитектура?”

Ну и по традиции мы начнем с определения … сначала определения архитектуры. Ниже представлены два разных определения, первое было дано Гради Бучем 15 лет назад и акцентирует внимание на важных решениях, которые сложно/дорого изменить. А вот второе определение было дано Филиппом Крачтеном на вручении SEI Software Architecture Award 2020 и оно акцентирует внимание на границах и траектории развития системы. Второе определение лучше укладывается в контекст этой статьи.

Рис. “Определения архитектуры”

Но кажется, что стоит отдельно обсудить, а что значит слово эволюционная в заглавии нашей статьи.

Рис. “А что такое эволюционная архитектура?”

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

Рис. “Эволюционная архитектура — инкрементальные и управляемые изменения”

Инкрементальность изменений позволяет получить профит на этапе build и deploy. С точки зрения билдов маленькие изменения проще имплементировать, чем большие.

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

В итоге, эволюционная архитектура состоит из трех частей.

Рис. “Три части эволюционной архитектуры”

Давайте рассмотрим каждую часть отдельно и начнем с инкрементальных изменений.

Рис. “Инкрементальные изменения”

В современном Agile мире инкрементальность изменений являются стандартом де-юре:) Обычно это подается под соусом Lean Startup, где предлагается быстро тестировать бизнес-гипотезы, получать результаты экспериментов и принимать решения. Концепция звучит хорошо, но на уровне разработки такие бесконтрольные итерации часто превращают структуру ваших приложений в спагетти, которое может проявляться как на уровне исходного кода, так и взаимодействия отдельно развернутых сервисов посредством публичных API:)

Рис. “Итог бесконтрольных инкрементальных изменений”

Это не является проблемой, если мы делаем прототипы для проверки гипотез и планируем их в дальнейшем выкинуть (и главное следуем этим планам). Но если мы делаем не прототип, то нам надо уметь контролировать то, что изменения потихоньку не запутывают нашу систему и не выводят ее за границы желаемой архитектуры и критериев, сформулированных в нефункциональных требованиях. В этом нам могут помочь fitness functions.

Рис. “Fitness functions”

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

Рис. “Определение архитектурных fitness functions”

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

Рис. “Архитектурные характеристики”

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

Рис. “Fitness function fit”

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

Изначально в разработке программного обеспечения речь часто шла по функциональные и нефункциональные требования. Первые приносили продакты или бизнес аналитики, а вот вторые оставались за инженерной командой (системные аналитики, архитекторы, разработчики). В силу того, что функциональные требования были прямым заказом продакта, а нефункциональные требования изначально им не заказывались, то и внимание им уделялось не так много. Лейтмотивом такого отношения являлась фраза “нефункциональные требования я не заказывал, в отличие от функциональных”.

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

Финальной трансформацией на этом пути стало название архитектурные характеристики, многие из которых на английском языке оканчиваются на ilities, например, high availability, maintainability, auditability, usability и так далее. Суть в том, что эти нефункциональные требования или атрибуты качества напрямую влияют на архитектуру системы и ее характеристики. Да и звучит солидно:)

Теперь, после короткого отступления вернемся к теме fitness functions, которые помогают с контролем того, что значения архитектурных характеристик находятся в желаемых пределах.

Рис. “Комбинация индивидуальных fitness functions формирует общесистемную fitness function”

Комбинация индивидуальных fitness functions формирует общесистемную fitness function. Это могут быть комбинации разных видов тестов (юниты, контрактные, интеграционные), метрик (процессных, архитектурных), мониторинг работы систем и алертинг на важные события.

Сами fitness functions можно разделить на следующие категории, которые отличаются степенью важности для архитектуры системы.

Рис. “Категории fitness functions”

Для получения профита от fitness functions нам требуется описать их , а дальше автоматизировать их запуск. Желательно прогонять их достаточно часто, идеально на каждое изменение, условно каждый pull request в мастер ветку. Вот стандартные инструменты, которые могут помочь вам в этом сложном деле.

Рис. “Инструментарий для автоматизации проверки fitness functions”

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

ArchUnit is a free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework.

Другой инструмент — Danger, который позиционирует себя так

Danger runs during your CI process, and gives teams the chance to automate common code review chores.

Правила для Danger можно писать на разных языках, так что можете выбрать свой любимый.

Fitv — это наш внутренний инструмент, который расшифровывается как Fitness Validator. Этот инструмент мы сделали для направления архитектуры и контроля технического долга в команде, которая занималась распилом небольшого монолита на десятки сервисов. Очень хотелось, чтобы соблюдение архитектурных подходов в этих сервисах контролировалось не просто глазами технических лидов, а проверялось в CI/CD пайпланайнах соответствующих команд.

Подробнее про автоматизацию соблюдения архитектурных принципов можно посмотреть в докладе Мирослава Малкина с конференции ArchDays 2020.

А теперь перейдем к последней части эволюционной архитектуре, а именно к подходящему coupling компонентов в системе.

Рис. “Подходящий coupling”

Здесь первым делом надо вспомнить про принципы модульности: Cohesion и Coupling. Про эти принципы подробнее можно прочитать в статье “Essential Architecture #Code”.

Рис. “Определение модульности”
Рис. “Определение cohesion и coupling”

Дальше нам интересны принципы организации компонентов, которые приводил дядюшка Боб (Uncle Bob) в своей книжке “Clean Architecture” (подробнее можно прочитать в моем обзоре второй части этой книги “Часть II: принципы дизайна модулей и разделения по компонентам”).

Рис. “Принципы организации компонентов системы”

Мы рассмотрим только два принципа из шести, чтобы подробнее поговорить про стабильность и абстрактность компонентов. И начнем со стабильности. Принцип Stable Dependencies Principle говорит про направление зависимостей, которые должны вести в сторону более устойчивых компонент. На рисунке ниже дано определение того, как считать Instability (нестабильность) компонентов, а также показан пример того, как должен выглядеть граф зависимостей в идеальном случае. И второй принцип из этой группы — Stable Abstraction Principle, который говорит про то, что компонент должен быть настолько же абстрактным, насколько он стабилен. На рисунке ниже представлен алгоритм расчета метрики абстрактности компонентов через количество абстрактных классов в компоненте по отношению к общему количеству классов

Рис. “Принципы стабильности и абстрактности компонентов”

Ну и теперь мы можем визуализировать Stable Abstraction Principle как показано на рисунке ниже. Суть в том, что хорошие компоненты должны группироваться в районе The Main Sequence. Кроме того есть две особые зоны: зона боли и зона бесполезности. В зоне боли у нас идет завязка на конкретные реализации внутри стабильных компонентов. Это приводит к боли при попытке расширить систему. А в зоне бесполезности у нас есть нестабильные абстрактные компоненты, которые в принципе не имеют смысла — зачем нужны интерфейсы, которые никто не использует?

Рис. “Зона боли, зона бесполезности и The Main Sequence для компонентов”

В итоге, эволюционировать систему надо так, чтобы все меньше компонентов оказывалось в левом нижнем и правом верхнем углу, а вообще лучше, чтобы они были в районе The Main Sequence.

Теперь, когда мы рассмотрели основные принципы эволюционной архитектуры, пришло время поговорить о том, когда стоит начинать эволюционировать.

Рис. “А как понять, что пора?”

Для этого можно использовать триггеры потребности в эволюции из книги Team Topologies, подробнее в моем кратком обзоре этой книги в части про Evolving team interactions.

Рис. “Триггеры”

Но первым делом стоит понять как эволюция взаимодействия команд связана с эволюцией архитектуры. В ответе на этот вопрос нам поможет закон Конвея

Рис. “Закон Конвея и связь структуры команд и архитектуры”

Теперь мы можем перейти к самим триггерам, которые перечислены ниже.

Рис. “Триггеры изменений”

Вот какие симптомы проявляются в случае чрезмерного размера софта.

Рис. “Симптомы чрезмерно большого софта для одной команды”

Следующие симптомы относятся к скорости поставки.

Рис. “Симптомы скорости поставки”

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

Рис. “Симптомы чрезмерной дополнительной когнитивной сложности”

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

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

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Written by Alexander Polomodov

Technical Director @T-Bank, Department of client interfaces, marketing and engagement.

No responses yet

Write a response