Слои, порты и адаптеры, CQRS
Алина открывает главу про архитектурные паттерны: бизнес-логика важна, но не менее важно, как она организована, и первый ответ — слоёная архитектура из презентации, сервисного слоя, бизнес-логики и доступа к данным, где хранилище — не только база, но и S3. Вова вспоминает первое место работы: пакеты dao, services и controllers, Spring с XML-конфигурацией — и уверенность, что любой бэкенд пишется именно так. Александр замечает, что Влад начинает с трёх слоёв, а сервисный добавляет по необходимости — когда логика на Active Record требует транзакционности поверх нескольких объектов; при Transaction Script слой вырождается. Два уточнения важны: слои — логические границы внутри одного рантайма, а tiers — физические со своим жизненным циклом; зависимости идут сверху вниз, значит бизнес-слой зависит от инфраструктуры, и доменную модель на нём реализовать трудно. Илья вспоминает инструменты вроде ArchUnit и jQAssistant, а Александр — свой доклад про эволюционную архитектуру и пример проверки таких соглашений линтером на Elixir.
Порты и адаптеры инвертируют зависимость: бизнес-логика объявляет порты-интерфейсы, а инфраструктура — объединившая презентацию и доступ к данным — реализует их адаптерами. Александр подчёркивает, что это прямое следствие принципа инверсии зависимостей и что для Влада гексагональная, луковая и чистая архитектуры — одно и то же; сервисный слой здесь называют application layer, и участникам нравится, как автор явно перечисляет синонимы, чтобы не плодить «сэндвич» терминов. Дальше CQRS: полиглотное моделирование — одни данные для поиска в Elasticsearch, другие для аналитики — и поверх событий строятся представления под каждую задачу. Модель исполнения команд строго консистентна, read-модели — eventually, и Влад советует начинать с синхронных проекций с чекпоинтами, а асинхронные заводить, когда упрётесь в масштаб: иначе с головой ныряешь в распределённые системы без причины. Красная линия всех трёх встреч, по словам Вовы, — «есть куча инструментов, выберите самый простой», причём паттерн выбирается для конкретного контекста, а не для всей организации.
Трансляция моделей и интеграция агрегатов
Александр напоминает стратегические паттерны — партнёрство, общее ядро, customer–supplier — и объясняет, что в главе о коммуникации Влад объединяет защитный слой и открытый хост в одну тему: трансляцию моделей между контекстами. Илья разбирает варианты: stateless — прокси, который на лету переводит модель и может нести версионирование, хоть на облачном API-шлюзе; stateful — агрегатор, который собирает несколько запросов в batch и почти всегда нуждается в хранилище, будь то база или кэш. Алина добавляет, что stateful-трансляция может жить на стриминге вроде Kafka Streams или на батчах в Spark — прямое продолжение глав Клеппмана. Александр показывает своё дерево опций по принципу MECE: синхронно или асинхронно, stateless или stateful, входящие или исходящие данные — и признаётся, что именно оно объяснило, чем его беспокоили предложения «сделать BFF поверх API-шлюза»: без чётких критериев границы применимости подходов расплываются.
Интеграция агрегатов начинается с того, как правильно публиковать доменные события, и Влад честно проходит по типичным ошибкам. Публикация из агрегата до коммита: событие ушло, а транзакция откатилась. Публикация из application layer после коммита: между записью и отправкой сервис может упасть, и об изменении никто не узнает. Илья видел вариацию — самописный transaction manager, копивший события до коммита, — с тем же изъяном. Ответ — outbox: события пишутся в ту же транзакцию, отдельный relay читает их и публикует, помечая отправленные; в худшем случае получаются дубликаты и семантика at-least-once, с которой клуб уже умеет жить. Вова отмечает вариант для NoSQL — кусочек outbox прямо внутри документа — и что публикация бывает push и pull. Александр вспоминает, как в одной из систем рекламной платформы события снимали с журнала транзакций базы — то, что Влад называет log tailing.
Сага, process manager и что дальше
Сага нужна, когда бизнес-транзакция проходит через несколько агрегатов — Александр приводит пример с созданием пользователя, песочницы, привязкой оплаты и подпиской. Настоящей транзакции нет: компонент слушает события одного агрегата и публикует команды другим, при неудаче — повтор или компенсирующая транзакция, например вернуть списанные деньги. Long-running здесь означает не время, а цепочку отдельных транзакций, и сага гарантирует лишь eventual consistency: система движется к сходимости или откатывается, застывая по пути в промежуточном состоянии. Слишком частые саги для синхронизации агрегатов — признак неверно проведённых границ, и Вове нравится формулировка Влада, что сагой можно проверять границы контекстов. Это дорогое удовольствие для околоденежных процессов, где нельзя оставлять пользователя разбираться с поддержкой; Александр вспоминает, как коллеги из Avito рассказывали о своей реализации саг.
Process manager Алина описывает как сагу на стероидах: сага гвоздями прибита к событиям, а process manager — конечный автомат с логикой if/else, который сам решает следующий шаг. Пример из книги — командировка: менеджер предлагает самый дешёвый перелёт, сотрудник может отказаться, тогда нужен аппрув руководителя, затем ищется отель, и если его нет — отменяется всё. Слушатель прислал замечание, что сагу принято рассматривать прежде всего как оркестрируемую; Александр отвечает, что в исполнении Влада сага ближе к хореографии, а process manager — к оркестратору, и советует сравнивать авторов: границы инструментов зыбки, а разные названия часто скрывают одни принципы. На вопрос про complex event processing он вспоминает Клеппмана: это запросы, вывернутые наизнанку, — сначала пишешь правило, потом на него льётся поток событий, что подходит для fraud detection. Следующая встреча — про то, как натягивать все эти паттерны на практику, и немного про EventStorming.
Что стоит унести с собой
- 01Слоёная архитектура годится для Transaction Script и Active Record, но её зависимости сверху вниз мешают доменной модели; порты и адаптеры инвертируют их, и для Влада гексагональная, луковая и чистая архитектуры — синонимы.
- 02CQRS начинают с синхронных проекций с чекпоинтами, а асинхронные заводят под реальный масштаб; паттерн выбирается для конкретного контекста, а не для всей организации.
- 03Публиковать события до коммита или сразу после — ошибка с потерей данных или события; outbox с relay даёт at-least-once, а log tailing снимает события с журнала базы.
- 04Сага и process manager дают только eventual consistency через компенсации; частые саги для синхронизации агрегатов — сигнал пересмотреть границы.