К основному содержимому
к дискуссии
Конспект дискуссии2024

Рулетка кейсов: примеры плохой архитектуры

Шестой сезон Podlodka Techlead Crew, формат «рулетка кейсов»: ведущие Игорь Антонов и Павел зачитывают тезисы, а три эксперта — Александр, технический директор Тинькофф, Илья Зонов, архитектор Тинькофф Бизнеса, и Кирилл, стаф-инженер Сбермаркета, — по очереди вспоминают байки про универсальную архитектуру, CQRS, GraphQL, ORM, свои стандарты и микросервисы.

Podlodka Techlead Crew7 минут

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

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

Хорошая архитектура и универсальные решения

Разговор начинается с определения. Кирилл отвечает классически: хорошая архитектура позволяет делать бизнес, отвечает функциональным и нефункциональным требованиям и остаётся гибкой — изменения приходят быстрее, чем успеваешь рефакторить, поэтому ценна скорость перехода к новой схеме, а не идеальное состояние. Александр напоминает, что книги по архитектуре начинаются с перебора определений — компоненты, связи, важные свойства, — и что архитектура «под любые будущие изменения» окажется слишком абстрактной и бесполезной в текущем домене: через полгода сегодняшнее хорошее решение перестаёт быть хорошим. Игорь вспоминает статью на сайте IBM, где автор насчитал около 154 определений архитектуры ПО. Илья напоминает: ограничения архитектура вводит всегда, важно уметь их контролировать — тут же вспоминают фитнес-функции. Павел признаётся, что его команда не смогла сформулировать определение и выписала аспекты, на которые архитектура влияет: надёжность, стоимость поддержки, эффективность ресурсов, time to market — это хотя бы измеримо.

Первый кейс — универсальная архитектура, которую обещают на секциях по системному дизайну. Кирилл отшучивается: если нужна система, где в любую колонку пишется что угодно, а остальное дописывается макросом, разработчики не нужны — берите Excel. Александр вспоминает, как лет семнадцать назад заказчик, послушав про модель предметной области и MVC, попросил «универсальный контроллер», который свяжет любую бизнес-логику. Второй пример — Amazon Prime Video: обработку видео собрали каноническим способом, на Step Functions и S3, и она вышла на порядок дороже, чем обработка куска видео in-memory. За границей применимости универсальное решение фейлит, а инженер с одним молотком забивает им и шурупы. Илья приводит аудит движков для сборки форм: форма для оператора и форма для клиента не сводятся друг к другу — разные цели, требования и аудитория. Его собственный промах — попытка сложить всё, включая расписание телепередач, в универсальное time-series-хранилище поверх Cassandra; данные потом переложили в реляционную базу. Кирилл защищает перочинный нож: на стройке бесполезен, в лесу спасает, а на стадии MVP гибкое приложение или no-code помогает бизнесу.

02

CQRS и GraphQL: границы хайпа

Следующий тезис — когда CQRS вреден. Кирилл ссылается на книгу Влада Хононова по domain-driven design с блок-схемой: нужен ли вам CQRS или хватит transactional script, зависит от типа домена — ядро это, поддерживающий или generic; в поддерживающем паттерн только добавит сложности и отъест time to market. Александр говорит, что не припомнит CQRS, сделанного с самого старта: обычно это ответ на боль, когда приложение доросло до множества обновлений, чтений и многоуровневого кэша, и разделение команд и представлений для чтения даёт систему на порядок производительнее. Делать так сразу — риск преждевременной оптимизации ради хайповой технологии, поэтому на такое предложение он задаёт много вопросов. Кирилл раскладывает паттерн на три уровня: разделение команд и запросов в коде он использует каждый день, разделение на две базы данных сильно помогло, когда пошла реальная нагрузка, а сам паттерн он применяет для поиска по данным нескольких транзакционных сервисов. Павел напоминает, что события приходят с задержками и ломают консистентность.

Про GraphQL Илья честно говорит, что в одном проекте тот работал нормально, а среди собравшихся, судя по всему, на этот хайп никто не пошёл. Александр разбирает происхождение: ставка на мобильный веб не сработала, а собирать данные для нативных приложений из кучи API было тяжело — так появился GraphQL. По его мнению, с тем же успехом можно выставить в интернет SQL: декларативный язык прекрасен для фронтенда, но на бэкенде контролировать сложность тяжело, потому что клиент сам решает, что запросить. В его спецпроектах GraphQL воткнули вместе с движком Prisma, который в той версии был однопоточным и лочил источники данных: приложение падало и лежало, движок выпилили, а сам подход превратился в карго-культ. Кирилл описывает свежую гипотезу — для аналитики, а не для транзакционных сценариев, GraphQL даёт потребителю читать нужные поля из любого сервиса, — но под высокой нагрузкой не советует. Илья брал его, чтобы не таскать целиком кредитные заявки.

03

Репозитории, стандарты и микросервисы

Спор про ORM и репозиторий Кирилл снимает сразу: это разные паттерны. ORM отлично работает на изменениях агрегата — если в корзине добавили, изменили и удалили позиции и поменяли адрес, она сама поймёт, что менялось, и сохранит всё одной транзакцией; но для чтения объекты не нужны, дешевле обычный запрос в DTO. Репозиторий отвечает за целостность: один агрегат — один репозиторий, иначе кто-нибудь однажды забудет сохранить статус корзины, — а в запросах на чтение он превращается в карго-культ. Александр вспоминает, как больше десяти лет назад переписал слой хранения нагруженного сайта, крутившегося на четырёх машинках в одной стойке: подрядчики перед запуском навесили кластерные индексы, вакуум довершил дело, деградация была видна на дашбордах — спасла комбинация репозиториев с кэшем, который обновлялся в момент сохранения. Павел добавляет, что на чужом проекте ORM снижает неопределённость, а в своём коде он предпочитает голый SQL и знает, попадёт ли запрос в индекс.

Про свои стандарты Александр вспоминает поговорку про чужой монастырь: попытка объединить десять существующих стандартов рождает одиннадцатый. Плюсы стандартов — тулинг и переносимость людей между проектами, как readability в Google; минус — потеря веры в них там, где консенсуса нет. Илья добавляет, что без тулинга и явных бенефитов стандарт умирает, а Кирилл, отвечающий за стандарты в своей компании, советует в 90% случаев брать существующие практики. Последний тезис — микросервисы на старте. Илья запускал лизинг монолитом с границами внутри, потому что домен знали плохо, и часть границ всё равно провели неверно. У Александра партнёрский сервис нарезали по сущностям, а команда потом склеила десять-двенадцать сервисов обратно в три; в другой истории админка со всем сразу уронила приложение, распил занял полгода, а общий Rancher не выдержал нагрузки и счёл хосты недоступными — контейнеры подняли docker-compose на старых машинах. Кирилл спрашивал у команды из четырёх человек, зачем им шестнадцать микросервисов: к его уходу осталось два.

Выводы

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

  1. 01Универсальность живёт до границы применимости: канонический пайплайн на Step Functions и S3 обошёлся Amazon Prime Video на порядок дороже in-memory-обработки видео.
  2. 02CQRS редко делают с нуля: разделение команд и запросов в коде дёшево и полезно всегда, разделение баз — ответ на пришедшую нагрузку, а полный паттерн оправдан для поиска по нескольким сервисам.
  3. 03ORM выигрывает на изменениях агрегата, чтение дешевле обычным запросом; репозиторий нужен там, где есть доменная модель, а в запросах на чтение он превращается в карго-культ.
  4. 04Микросервисы делят зоны ответственности между командами, а не ускоряют разработку: шестнадцать сервисов на четверых и склейка десяти-двенадцати сервисов обратно в три — цена дробления до знания домена.

Источники