К основному содержимому
к выпуску
Конспект выпуска2022Fellow

Software Architecture: The Hard Parts — выпуск 1

Клуб начинает новую книгу, выбранную голосованием слушателей, — Software Architecture: The Hard Parts Нила Форда, Марка Ричардса, Прамода Садаладжа и Жамак Дехгани. Илья, Александр и Владимир Чистяков разбирают три первые главы: почему решения «hard», что такое ADR и fitness functions, из чего складывается архитектурный квант и какие драйверы заставляют делить систему на модули.

Code of Architecture · книжный клуб7 минут

Конспект сокращён относительно полного выпуска; примеры и оценки передают опыт и позиции участников.

Основная линия материала
01

Почему части «hard» и чем их контролировать

Книга выросла из Fundamentals of Software Architecture: Форд и Ричардс откладывали сложные вопросы, которые туда не поместились, и позвали двух соавторов, занимающихся данными; статью Дехгани о data mesh клуб недавно добавлял в свою архитектурную рассылку, а с Владом Хононовым обсуждал data mesh две недели назад. Александр показывает, как авторы обосновывают название: у слова hard две коннотации — трудные решения и фундаментальное, то, что потом сложно изменить; архитектура отвечает на «что», дизайн — на «как», и дизайн остаётся за скобками. Первая глава щедро ссылается на другие книги, вводит два вида данных — операционные, без которых бизнес не работает, и аналитические — и напоминает, что данные живут дольше систем. Вова добавляет из разговора с Владом: там, где данные критичны, помогает event sourcing с единым источником истины. Илья рассказывает про ADR — запись решения с контекстом, причинами и последствиями, которую клуб сам применяет для больших изменений; Александр советует второе издание книги автора ADR, где архитектура рассмотрена с точки зрения эксплуатации.

Договорившись в ADR, нужно проверять, что ничего не развалилось, — для этого fitness functions: любой механизм, дающий объективную оценку одной или нескольких характеристик архитектуры, например JDepend и ArchUnit для поиска циклических зависимостей. Александр вспоминает Building Evolutionary Architectures 2017 года, где фитнес-функциями называли всё, что тестируется, и предупреждает: легко говорить, трудно сделать — что-то сложнее циклов и цикломатической сложности, вроде оценки связанности по Uncle Bob, полностью не автоматизируется, кто-то должен разбирать результат. На конференции он показывал команду, которая переложила такие проверки на Elixir с хорошо описанным синтаксисом, и обещает ссылку. Полезная эвристика книги: если для функции нужны доменные знания, это интеграционный тест, а не фитнес-функция. Илья замечает, что связанные характеристики — security и performance — балансируют, а не оптимизируют по отдельности, и Александр видит в этом смысл holistic-функций: зафиксировать компромисс и следить, что нет перекоса. Цепочка «нефункциональные требования → атрибуты качества → архитектурные характеристики» — по сути способ продажи бизнесу: характеристики, влияющие на time to market, продавать проще, чем требования с отрицанием в названии.

02

Архитектурный квант

Сквозной пример книги — сага о Sysops Squad: монолит службы поддержки, которому бизнес грозит закрытием из-за срывов сроков и потерь данных, и заметки, где архитекторы обсуждают решения. Илье он кажется ненастоящим — слишком плохой монолит и бизнес, принимающий всё без возражений; Вова добавляет, что архитектор в книге — главный герой и спаситель, как инженер в «Проекте Феникс» и его продолжении. Вторая глава открывает первую часть — анализ, разделение на кусочки — и вводит архитектурный квант: независимо развёртываемый артефакт с высокой функциональной связностью, высокой статической связанностью и синхронной динамической связанностью. Примеры: монолит вместе с базой — один квант, потому что без базы он не стартует; service-based архитектура вокруг общей базы — тоже один квант, и миграция ломает забытый четвёртый сервис; событийная архитектура с общей базой — то же; сплошные линии на диаграммах означают синхронные вызовы, пунктирные — асинхронные, чтобы не рисовать очереди. Микросервисы — по кванту на сервис, но общий пользовательский интерфейс склеивает их обратно, и авторы вспоминают микрофронтенды; общая база между системами склеивает даже независимые сервисы.

Перед эфиром Илья и Александр спорили, почему определение не работает до конца: если сервисы завязаны на кластер Kafka и без соединения не стартуют, попадают ли они в один квант? Илья считает, что retry, идемпотентность и fallback снижают связанность и позволяют разделить кванты; Александр уточняет определение авторов: синхронным считается взаимодействие, при котором вызывающий останавливается и ждёт ответа, и если сервис не может работать, не получив данные, это синхронность независимо от транспорта — Kafka с ожиданием ответа не даст характеристик асинхронности. Он показывает свою схему главы: квант раскладывается на четыре части, а динамическая связанность — на три измерения: стиль коммуникации, консистентность и координация — оркестрация или хореография, — и приводит трёхмерную таблицу авторов с забавными названиями восьми комбинаций. Илья отмечает параллель между квантом и ограниченным контекстом из DDD: квант пошёл дальше, добавив статическую и динамическую связанность, но исходное соответствие прослеживается.

03

Драйверы модульности

Третья глава начинается с правильного вопроса: архитекторы в сюжете хотят распилить монолит, потому что всё остальное не помогло, и их разворачивают — сначала объясните зачем. Бизнес-драйверы — time to market и конкурентное преимущество, слияния и поглощения, новые рынки; Александр развивает: устоявшуюся компанию всегда окружают стартапы с более удобными сценариями, и медленные изменения оставляют на месте динозавра, который через минуту понимает, что его грызут. Из скорости вытекают availability и fault tolerance, а из непредсказуемой нагрузки — масштабируемость. Технические драйверы: сопровождаемость через связанность, цикломатическую сложность, размер компонентов и деление по доменам вместо технических слоёв; тестируемость — автоматизация и полнота покрытия сценариев; развёртываемость; масштабируемость, которую авторы отличают от эластичности: первая — как климат с постоянным ростом пользователей, вторая — как погода с дневными пиками и концертом популярной группы, и для неё приложение должно быстро стартовать; и отказоустойчивость.

Примеры идут по характеристикам. Сопровождаемость: в слоёном монолите фича задевает UI, бэкенд и базу и требует три команды; при делении по доменам затрагивается меньше, а в микросервисах функцию поддерживает одна команда, хотя из-за UI и микрофронтендов их всё равно может быть две. Тестируемость: изменение в одном сервисе кажется локальным, но динамические связи A → B → C заставляют проверять всю цепочку, а изолированный модуль позволяет гонять небольшой набор тестов вместо гигантского. Развёртываемость: деплой одного сервиса из кванта с десятками сервисов рискует поехать по цепочке, потому что в монолите нет явных контрактов, и появляются код-фризы. Отказоустойчивость: чем больше квант, тем больше домен отказа — Вова сравнивает с blast radius; балансировщик и два инстанса монолита не спасают, потому что баг в одном инстансе выбивает второй, как в игре на выбывание. Следующий выпуск — архитектурная декомпозиция и component-based decomposition, в сюжете как раз появляется первый ADR.

Выводы

Что стоит унести с собой

  1. 01«Hard» — это и трудные, и фундаментальные решения; их фиксируют в ADR, а соблюдение проверяют fitness functions: если для проверки нужны доменные знания, это тест, а не фитнес-функция.
  2. 02Архитектурный квант — независимо развёртываемая единица с высокой связностью и синхронной связанностью; общая база или общий UI склеивают сервисы в один квант.
  3. 03Синхронность определяется ожиданием ответа, а не транспортом: Kafka с ожиданием не даёт асинхронности; динамическая связанность имеет три измерения — коммуникация, консистентность, координация.
  4. 04Делить систему нужно от драйверов — time to market, сопровождаемости, тестируемости, развёртываемости, эластичности и домена отказа, — а не потому, что остальное не помогло.

Источники