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

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

Четвёртый выпуск открывает вторую часть книги — как соединять распиленное обратно. Илья, Александр и Вова Чистяков разбирают главы про переиспользование кода и владение данными: копипаста, общие библиотеки, общие сервисы и сайдкары с service mesh, принципы компонентов Uncle Bob, кто пишет в общую таблицу, ACID против BASE и три способа прийти к eventual consistency.

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

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

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

Четыре способа переиспользовать код

Глава начинается с DRY против WET и четырёх подходов: репликация кода, общие библиотеки, общие сервисы и сайдкары. Репликация — это копипаста, которая красиво звучит: контекст максимально изолирован, ничего не шарится, зато изменения не распространить и не версионировать; пример авторов с аннотациями Вова находит странным. Александр вспоминает первое место работы, где на сайте было полсотни вариаций работы с датами и правка одной оставляла остальные, — после этого он прочитал «Совершенный код». Общие библиотеки закрывают версионирование и ловят ошибки на этапе компиляции, но приносят dependency management, дублирование на каждом языке в гетерогенной среде и вопрос устаревания версий: стратегия поддержки старых версий зависит от частоты релизов, как у дистрибутивов Linux, а в распределённой системе непонятно, кому сообщать о новой версии.

Александр дополняет главу принципами компонентов Uncle Bob из Clean Architecture, которые считает анализом посильнее авторского. Release/Reuse Equivalence — компонент выпускается версиями, обычно по SemVer; Common Closure — в компонент собирают то, что меняется по одной причине в одно время, и разделяют то, что меняется по разным; Common Reuse — не заставлять пользователя зависеть от того, что он не использует, продолжение принципа разделения интерфейсов. Принципы образуют треугольник «выбери два из трёх»: забудешь про reuse — библиотеку сложно переиспользовать и найти нужное; про closure — каждое изменение растягивается по многим компонентам; про release — склеенный «всё включено» компонент релизится каждые три часа, потребители читают changelog и в итоге перестают обновляться. Пример из книги — изменили маленький компонент и задели два сервиса вместо всех — ровно иллюстрация Common Reuse.

02

Общие сервисы, сайдкары и стабильная абстракция

Общий сервис Илья разбирает как ответ на гетерогенность: одна реализация логики за контрактом, а не библиотеки на каждом языке, и один деплой вместо распространения версий. Цена — ошибки уезжают из компиляции в runtime, версионирование API с вопросом, сколько версий поддерживать, отказоустойчивость, потому что при недоступности сервиса логики лишаются все, производительность, упирающаяся в сеть, и необходимость масштабировать этот сервис отдельно. Илья вспоминает проект, где применили все три подхода сразу и собрали все недостатки: сложную дистрибуцию копипасты, частые обновления библиотек и runtime-ошибки сервиса. Сайдкары авторы объясняют слабовато, и Александр рассказывает через Kubernetes Patterns: базовая сущность — pod из нескольких контейнеров; init-контейнер готовит приложение к старту, sidecar расширяет его без изменений — композиция вместо наследования, например перехват логов из файла в stdout; adapter унифицирует гетерогенную систему, ambassador отрезает приложение от внешних зависимостей, как порты и адаптеры.

Смысл сайдкара в том, что это отдельный процесс на том же хосте: ходишь к нему по localhost, но нет ни проблем библиотеки с дистрибуцией версий, ни сетевой латентности общего сервиса, а обновляет его платформенная команда — так работает service mesh с control plane и data plane перед каждым сервисом. Вова уточняет: авторы предлагают сайдкары для операционных вещей — логирования, мониторинга, — а не для доменных библиотек, чему герои книги как раз противились. Александр добавляет ортогональные зависимости из главы: сайдкар отделяет операционную ось от бизнес-логики. Главный его вывод шире книги: переиспользование работает, когда абстракция стабильна, а при высокой скорости изменений — в core-домене — общее решение приносит боль; платформенные команды и «платформа как сервис» не серебряная пуля, и если платформа движется как неуловимый Джо, пользователям будет некомфортно.

03

Кто владеет данными и как прийти к согласованности

Глава про владение данными: если в таблицу пишет один сервис — single ownership, вопросов нет. Common ownership — пишут почти все, как в аудит; решение — отдельный сервис-владелец, куда остальные шлют события асинхронно, хоть через Kafka. Joint ownership — несколько сервисов одного домена пишут разные поля, как каталог и инвентарь в таблицу продуктов, и здесь четыре техники. Разделить таблицу на две, если консистентность не критична. Data domain — оставить общую схему обоим, и Александр напоминает: в Enterprise Integration Patterns интеграция через базу — антипаттерн, где изменение схемы непонятно кого сломает; два сервиса на общей базе — плохо, три — очень плохо, четыре — совсем. Делегировать владение одному: по primary domain priority — тому, кто ближе к структуре данных и уже имеет для неё API, как каталог; или по operational characteristics — тому, кто чаще обновляет, как инвентарь; авторы предпочитают первое, добавляя кэш ради скорости, Александр смотрел бы на SLA. И консолидация — слить сервисы обратно: транзакции из коробки, но общий домен отказа.

Дальше распределённые транзакции: Вова напоминает ACID — атомарность, консистентность, изоляция конкурентных пользователей, устойчивость после выдернутого провода — и BASE из мира NoSQL: базовая доступность участников, мягкое состояние, видимое в полупозиции, и eventual consistency — если систему не трогать, она сойдётся, вопрос лишь на каком горизонте. Три паттерна достижения. Фоновая синхронизация: отдельный процесс копирует данные по таблицам других сервисов; Илья сравнивает с анти-энтропией у Клеппмана, но Александр видит проблему — это уже не инфраструктурный механизм, а процесс, который знает доменную семантику всех, смешение политики с механизмом. Оркестрированный запрос: в рамках запроса клиента обойти все сервисы и ответить, когда всё обновилось; ошибки и порядок вызовов усложняются, а доступность — Александр считает на лету — перемножается: три сервиса по 99,9% дают около 99,7%. Событийный: сервис публикует событие, остальные подписываются — подход, который клуб разбирал у Клеппмана и Влада, хотя авторы смешивают события и команды. Следующий гость — Игорь Маслов, руководитель базовых технологий.

Выводы

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

  1. 01Копипаста изолирует контекст, но не версионируется; общая библиотека ловит ошибки на компиляции, но требует стратегии устаревания и дублирования по языкам; общий сервис уносит ошибки в runtime и упирается в сеть.
  2. 02Принципы компонентов Uncle Bob — Release/Reuse, Common Closure, Common Reuse — образуют треугольник «выбери два», и это анализ сильнее авторского; переиспользование работает только на стабильной абстракции.
  3. 03Сайдкар — отдельный процесс на том же хосте под управлением платформенной команды: без дистрибуции библиотек и без сетевой латентности, но для операционных вещей, не для доменной логики.
  4. 04Общая таблица нескольких сервисов — интеграция через базу; лучше делегировать владение одному сервису по близости к домену или по SLA, а eventual consistency достигать событиями, а не оркестрацией с перемножением девяток.

Источники