Пригодность и компромиссы
Не существует лучшей архитектуры — только структуры, пригодные или непригодные для конкретных сил. «Ilities» конкурируют между собой: оптимизация одной стоит другой. Урок вводит B2B-пример заказов и биллинга, используемый на протяжении всего трека.
Две инженерные команды, одна проблема: растущая B2B-платформа, где заказы поступают в биллинг. Команда A реструктурировала кодовую базу в пятнадцать микросервисов. Деплои стали быстрее, но отладка расхождения в биллинге теперь требует трассировки через шесть сервисов, три очереди сообщений и распределённый трейс, который читается сорок минут. Команда B сохранила монолит, но добавила чистые внутренние модульные границы. Они не могут масштабировать движок биллинга независимо, но отладка занимает десять минут с одним стектрейсом. Ни одна из команд не ошиблась. Они оптимизировали под разные силы — и заплатили за это разную цену.
Рамка пригодности
Предыдущий урок установил, что архитектура — это набор решений, которые дорого менять. Этот урок устанавливает, что делает данный набор решений хорошим: пригодность под силы, которым подвержена система.
Структура пригодна, если она делает наиболее важные для этой системы качества легкодостижимыми и если качества, которыми она жертвует, для этой системы не важны. Та же структура, которая превосходна под одним набором сил, вредна под другим. Это не релятивизм — это точность. Невозможно оценить архитектуру, не зная сил, на которые она должна отвечать.
Силы приходят с двух сторон: функциональные требования (что система должна делать) и атрибуты качества (насколько хорошо она должна это делать и при каких ограничениях). Функциональные требования — более лёгкая часть для рассуждений: их можно специфицировать, тестировать и отслеживать. Атрибуты качества — там, где живут структурные решения.
«Ilities»: конкурирующие атрибуты качества
Атрибуты качества часто называют «-ilities» (от английского суффикса), потому что многие их имена оканчиваются на это сочетание: maintainability, deployability, testability, scalability, reliability, security, observability. Каждый описывает измерение, по которому можно оценить систему. Проблема в том, что они тянут в противоположных направлениях.
Maintainability — насколько легко можно изменить кодовую базу без внесения ошибок? Высокомодульная, хорошо абстрагированная база набирает здесь очки, но абстракция добавляет indirection и когнитивные накладные расходы.
Deployability — насколько быстро и безопасно изменение попадает в production? Микросервисы максимизируют её (каждый сервис деплоится независимо), но переносят связанность из кода в инфраструктуру: сервисные контракты, управление версиями и распределённые режимы отказов.
Testability — насколько легко можно верифицировать поведение в изоляции? Dependency injection и паттерны ports/adapters делают unit-тестирование тривиальным, но добавляют интерфейсные слои, которые могут скрывать, а не раскрывать замысел в простых случаях.
Scalability — может ли система обрабатывать растущую нагрузку? Stateless, секционированные сервисы масштабируются горизонтально, но требуют, чтобы домен был сформирован вокруг схемы разбиения, что конфликтует с едиными доменными моделями.
Observability — можно ли понять, что делает система, когда что-то идёт не так? Монолит с хорошим логированием часто более наблюдаем, чем распределённая система, где один запрос трассируется через двенадцать сервисов.
Каждое архитектурное решение одновременно двигает несколько «ilities». Вынос billing в отдельный сервис улучшает deployability и scalability компонента billing; ухудшает observability и увеличивает поверхность для распределённых сбоев. Выбор — не «что лучше», а «какой регулятор наиболее важен для сил этой системы прямо сейчас».
▸Почему это работает
Почему «ilities» конкурируют, а не дополняют друг друга? Потому что они не свойства кода в изоляции — они свойства системы в контексте её организации разработки, инфраструктуры деплоя и операционной среды. Изменение, улучшающее deployability (развязанные сервисы), ухудшает debuggability (распределённые трейсы). Изменение, улучшающее testability (dependency inversion), ухудшает простоту (больше интерфейсов). Напряжение структурно: оптимизация одного измерения часто требует жертвы другим, потому что лежащие в основе силы тянут в разные стороны.
Бегущий пример: B2B-платформа заказов и биллинга
На протяжении всего трека повторяется одна система-пример: B2B-платформа заказов и биллинга для SaaS-компании. Заказы поступают через клиентский API. Они проходят ценообразование, расчёт налогов и рабочие процессы согласования. Они поступают в биллинг, который генерирует инвойсы, применяет скидки и инициирует сбор платежей. Отчёты биллинга питают финансовые дашборды. Разрешение споров требует просмотра полной истории заказа.
Это реальный домен с реальными силами:
- Несколько точек входа: заказы поступают через API, webhook и внутренние admin-инструменты.
- Разные темпы изменений: правила ценообразования меняются ежемесячно; логика сбора платежей меняется редко.
- Требования соответствия: записи биллинга — это неизменяемые аудит-трейлы; GDPR применяется к данным клиентов.
- Давление интеграции: платформа интегрируется с тремя платёжными процессорами, двумя ERP-системами и порталом клиента.
- Структура команды: продуктовая команда владеет потоком заказов; команда finance-tech владеет биллингом.
Эта платформа — не игрушка. Она вернётся, когда будем рассматривать layered architecture (единица 03), hexagonal architecture (единица 04), DDD bounded contexts (единица 06), CQRS (единица 07), event sourcing (единица 08) и декомпозицию (единица 10). Домен остаётся прежним; структура меняется каждый раз, когда мы применяем другой набор сил.
Честное чтение компромисса
Стандартный инженерный провал при рассуждении об архитектуре — асимметричный компромисс: перечислить все преимущества выбранного подхода и ни одного из его издержек, затем перечислить все издержки отвергнутого подхода и ни одного из его преимуществ. Именно так команды приходят к микросервисам для решения проблемы скорости деплоя, а потом два года борются с распределёнными транзакциями и query fan-out.
Честно прочитанный компромисс имеет такую форму:
- Каким силам отвечает эта структура? (Назови их конкретно, не расплывчато.)
- Чего это стоит? (Назови «ilities», которые ухудшаются, и операционные накладные расходы, которые добавляются.)
- Каким силам эта структура не отвечает? (Будь явным в том, что ты принимаешь.)
- Что должно измениться, чтобы другая структура стала правильной? (Это эволюционный выход — если система столкнётся с силой, которую текущая структура не может обработать, что является триггером?)
Эта рамка появляется на протяжении всего трека. Это не чеклист для механического следования — это привычка явно называть силы перед оценкой структуры.
▸lesson.inset.note
Метод анализа архитектурных компромиссов (ATAM) формализует этот паттерн в структурированную технику оценки архитектурных решений по атрибутам качества. ATAM выявляет «точки чувствительности» — решения, сильно влияющие на конкретный атрибут качества, — и «точки компромисса» — решения, влияющие на несколько атрибутов качества в противоположных направлениях. Неформальная версия того же мышления — то, что делает каждый опытный инженер при ревью структурного предложения: «да, это улучшает X, но посмотри, что это делает с Y». Единица 11 рассматривает это глубоко.
Команда переходит от монолита к микросервисам для улучшения скорости деплоя. Через шесть месяцев они сообщают, что деплои быстрее, но отладка production-инцидентов теперь занимает втрое больше времени. Какая характеристика наиболее точна?
B2B-платформа заказов и биллинга нуждается в соответствии GDPR для данных клиентов, но финансовой команде также нужны неизменяемые аудит-трейлы каждого события биллинга. Эти два требования, кажется, конфликтуют: GDPR предписывает удаление, но неизменяемость запрещает его. Как это называется в рамке пригодности?
Senior-инженер на B2B-платформе отстаивает layered architecture, потому что 'она проста и все знают, как она работает'. Другой отстаивает hexagonal architecture, потому что 'это best practice для testability'. Как команде решить?
- 01Что означает 'архитектурная пригодность' и почему для неё нужно знать силы?
- 02Назови три пары «ilities», которые типично конкурируют, и объясни почему.
- 03Каковы четыре вопроса, составляющие честное чтение компромисса?
Не существует универсально лучшей архитектуры — только структуры, пригодные или непригодные для сил, воздействующих на конкретную систему в данное время. Эти силы делятся на функциональные требования (что система должна делать) и атрибуты качества (насколько хорошо она должна это делать при каких ограничениях). Атрибуты качества — maintainability, deployability, testability, scalability, observability, reliability — это структурные рычаги; каждое архитектурное решение двигает несколько из них одновременно и в противоположных направлениях.
B2B-платформа заказов и биллинга, введённая здесь, — заказы, поступающие через ценообразование и согласование в биллинг, с финансовой отчётностью, ERP-интеграцией и тремя платёжными процессорами — это бегущий пример, который этот трек реструктурирует. Домен остаётся постоянным; структура меняется по мере применения разных сил: layered architecture, hexagonal ports, DDD bounded contexts, CQRS, event sourcing и стратегии декомпозиции.
Честная оценка архитектурного выбора требует четырёх явных шагов: назови силы, которым она отвечает; назови стоимость, которую она извлекает; назови то, чему она не отвечает; назови триггер, который сделает другую структуру правильной. Эта рамка компромисса, а не каталог паттернов, — инструмент, который ты несёшь в каждое структурное решение.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.