Пробы на роль Архитектора. Акт III: выступление
Выборы архитектора происходят всегда по‑разному. Это только политиков одинаково выбирают — по принципу лишь бы не этот… А вот с архитекторами везде свой сценарий. Где‑то в половине мест сидят эстеты и предлагают что‑то изобразить да обсудить. Четверть мест явно химики с мензурками в сейфе — спрашивают про правильный код или просят пузырьковую сортировку. Ещё реже циркачи просят пожонглировать стеклянными шариками на башне или погонять трамваи. Остальные просто хотят обсуждения с залом. Как бы намекая, что нельзя вот так просто прийти в училище имени Гнесиных с фамилией Иванов. Кто‑то из гильдии таки должен сказать, что в мальчике что‑то есть.
У нас уже есть пережёванный легаси, набор грабель, методично разложенных под ногами и дальний свет сигнальных огней, к которым надо стремиться. Так что дело за малым — проложить дорогу от кучки легаси до тех самых огней по минному полю из грабель. Это и есть архитектура. Шагать вперёд, сверяться с картой, и быть чародеем.
Какую бы парадигму вы ни выбрали — важна целостность картины. Чтоб костюмчик одинаково хорошо сидел и в плечах, и в поясе. Если у вас в планах улей микросервисов в облаке, то скорее всего там хорошо впишется какой‑то PostgreSQL — доступно и не привязывает к конкретному облачному провайдеру. Все централизировано, автоматизировано, через удалённый доступ. А если вы планируете локальную развёртку, то продумайте о кластере MSSQL, разумном наборе ручных настроек и мониторингом раз на раз, так как бесперебойность работы системы уже не гарантируется инфраструктурой и будет зависеть от средней руки админа (той, что он положил на вашу систему). Естественно, выбирать вариант — корейский ресторан. Тот в котором вы собаку съели.
Новое — это незабытое старое. Раз уж я предположил, что легаси стоит on‑premises, то и nextgen буду вписывать в эту реальность. Не в каждой стране гос. учреждения и корпорации могут работать с публичными облачными сервисами. Ведь на временной линии события стоят в начале ковида и повсеместной удалёнки. Значит буду оставаться в реальности больших компонентов и готовых решений в локальной серверной. По условиям у нас неизменный начало и финальный блоки. Нет смысла и времени для поиска нового гениального решения. Остаёмся с конвейером. Зависимость между шагами никуда не денется, но можно поиграть с уровнем. Надо сделать возможным масштабирование. То есть вместо одного длинного каната между понтонами, нам нужно два коротких с креплением посередине. Так чтоб можно было цеплять и расцеплять понтоны не разрушая переправы. Размер не так важен как количество. Это я про связи. Межкомпонентные. На собеседовании такие аллегории излишни как факс в принтере. С профессионалами говорят на профессиональном. Ломанном английском. Так что мы тут собираемся строить что‑то невероятно великое и сложное... Пока не понятно что, но event driven.
Принцип построения на скорую руку:
стрелочки только односторонние,
сообщение через какой-нибудь message queue,
не раскрываем детали.
Будет ли у нас действительно Message Queue, Pub‑Sub с топиками или Event Bus — те самые неважные детали, о которых можно поговорить, но точно нет времени продумывать и изображать. По‑хорошему, это влияет на направление стрелок. Однако редко кто на это смотрит. Идеалы важны молодым, а я опытный. Я видел такое, что вам, людям, и не снилось. Паб-саб на поллинге. Многомегабайтные эвенты летящие с полным контекстом. Очереди без соблюдения очередности. Исключительно правильным остается правило, что в любом правиле, без исключений, есть исключения. Короче, режим Карлсона и на все вопросы: «Implementation details, дело‑то житейское!».
Первый набросок — это картинка из задания, переделанная в диаграмму из объектов PowerPoint разбивкой всех стрелок компонентом обмена:
Input → MQ → Repository → MQ → Processors → MQ → Categorization → MQ → Callout
Сразу же вторая итерация, так как есть два чёрных ящика:
Input → Repository → MQ → Processors → MQ(?) → Categorization → Callout
Раз категоризатор, как ребенок на заднем сидении, каждые минут пять спрашивает: «а мы уже приехали?», вне зависимости от происходящего, то инициатива вседа исходит от него. По идее можно переделать обращение в очередь или топик, вместо хранилища. В крайнем случае, всегда можно состряпать адаптер над очередью, имитирующий контракт хранилища из легаси. Эти размышления сразу натолкнули на мысль, что просто по голым событиям работать не получится. Хотя звучит пошло и привлекательно. Категоризатор не имеет интерфейса для регистрации и обратного вызова в pub‑sub. Плюс он ещё сразу получает весь контекст, а не просто оповещение, что произошло важно событие — сходи возьми вкусняшку». Значит всё‑таки централизированный MQ с persistence и подтверждениями (Guaranteed Delivery).
Вот от этого начинаем разгонять.
Теперь о целостности процесса — вводим правило, что все процессоры (Processing Engine) возвращают результат. И соответственно нам нужен смотрящий, который будет следить за тем, что упало и запускать заново. Похоже и в данном случае очередь сообщений будет хорошим вариантом. Менеджер будет брать сообщения, создавать процессоры и подтверждать изъятие из очереди после.
Стоп, а вот это «после», как менеджер об этом узнает? Тогда нужна двухсторонняя связь между Processing Manager и Processing Engine, которых он создаёт (процессы ли это, или наносервисы в контейнерах — implementation details). Очень этого не хочется. Прям кушать не могу. Давайте так, применим вариант дизайн паттерна заменяющего null значением. У нас будет один особый пустой Processing Engine, который отдаст ответ синхронно, после того как запишет в Repository метаданные, что процессинг стартанул. В диаграмме этого нет, но раз я задал вопрос себе сам, то им могут воспользоваться и другие. А значит надо быть готовым дать увесистый ответ. Processing Manager будет порождать только те Engine‑ы, результат которых отсутствует в опубликованных метаданных. Но не понятно, что делать с упавшими обработчиками. На данном этапе ставлю заметку, что повторное опубликование сессии в очередь лежит на Repository и тригериться либо по таймеру Х (как у них сейчас, а значит уже не хуже), либо через внешний мониторинг. Ведь у уважаемых донов должен быть какой‑то watchdog/healthcheck, чтоб мониторить своё хозяйство. Как говорят врачи: «рано или поздно мы все приходим к этой необходимости». Базовый DR у нас уже есть, и можно снять требование persistence и guaranteed delivery с очереди.
Мы навешали в Repository функцию publish/republish. Добавим туда и routing. В облаке было бы норм держать отдельные брокер и очереди, но для развертки на одомашненных серверах мне нужен минимум компонентов. Так что сервер сообщений один, а там будет пара очередей/топиков. Запись с неполной метой в очередь к Processing Manager, а с полной уже к Categorization.
Я не специалист в домене, поэтому понятия не имею что и как обрабатывается. Возможно сессия — это здоровый файл, который считывают последовательно, а возможно и набор чанков как в торенте, и обработчики их дергают в произвольном порядке. Так что нужна заготовка на все случаи жизни. Разделю хранилище данных на 3 базы:
Оперативная реляционная, куда пишут и читают мету. Значит тут и поиски, и индексы.
Объектное или файловое хранилище, куда публикуют сессию один раз, а читают много.
Хранилище обработанных сессий — блоб и мета одним документом. В задании не упоминали, что происходит после вызовов внешних апи. Но из оперативных данных надо убирать то, что не нужно, чтоб не сломать производительность. Так что вот тут будет cold storage. Или отчётики да метрики гонять, без последствий для текущих процессов.
С помощью такой нехитрой механики я получил state machine и фасад над данными. Первое важно для масштабирования — мы убрали необходимость хранить состояние в других модулях/сервисах. Второе для стабильности и предсказуемости — абстракция позволит менять вендора и при необходимости внедрить CQRS.
Теперь наш процесс выглядит примерно вот так:
Input → Repository → MQ → Processor Manager → Processors[] →
Repository → MQ → Categorization → Callout
Следующая итерация, как бы намекает, что раз уж мы разбиваем зависимости обменом через очередь, то почему бы не применить этот подход к каждой паре модулей:
Input → Repository → MQ → Processor Manager → Processors[] →
Repository → MQ → Categorization →
MQ → Callout
Так можно повысить пропускную способность закрытого Categorization модуля. Мы просто снимаем с него необходимость ждать ответа. А ведь в случае множества сервисов и неустойчивого соединения (скорее всего у нас там не LAN и не Cloud2Cloud) вызовы чреваты чрезмерно чревоточить таймаутами. Магия в том, что вызов внешнего сервиса не отличается от вызова своего же. То есть вместо прямого поста в чей‑то сервис АВС, мы постим в нашу очередь, тем самым размыкаем процессы категоризации и интеграции. А дальше уже новоиспеченные сервисы подхватывают из очереди и выполняют тот самый запрос в АВС. То есть нам, как и с процессингом, не помешает какой‑то менеджер создающий worker вызова.
Получаем новую картину мира:
Input → Repository → MQ → Processor Manager → Processors[] →
Repository → MQ → Categorization → Repository
MQ → Callout Manager → Callouts [] →
Тут уже проявляется паттерн. Когда одно решение MQ → Manager → Engine[] повторяется из раза в раз — это уже похоже на архитектуру Мандельброта, а значит я на верном пути!
По заданию категоризатор менять нельзя, поэтому я обыгрываю идею того, что модернизация — это лишь натягивание модного на каркас работающего. Судя по всему, весь свой контекст он получает извне и исполняет до конца. Получается, как бы стейтлесс гигасервис. То есть создавая дополнительные инстансы и управляя контекстом, можно масштабироваться в ширину. То, что было Categorization — станет Category Engine и нам понадобится менеджер, который подпишется на сообщения для общения и будет создавать и убивать Engine‑ы. А конфигурацию таймера переделать на минимум (если при старте легаси ждёт интервал до первого вызова) или максимум (если при старте сначала вызов, а потом ожидание). Таким образом пунктирный блок в целом должен убрать минусы, без изменения старого кода.
Где‑то в этой точке весь песок в часах перешёл в состояние покоя, а я в состояние тревоги. Что‑то с графиками приёмной комиссии не срослось и мне сообщили, что у меня есть еще один час. Его я потратил на разметку, раскрашивание и устранение ошибок.
В итоге получилось вот такое это вот:
Собеседование по Зуму во времена короно‑карантинов напоминало шоу Голос. Четверка архитекторов в жюри сидит, уткнувшись в соседний экран продолжая работать. Лучше сразу их отдёрнуть. Периодически спрашивайте простые вещи: нормально ли вас видно и как качество соединения на их стороне. Если позволить всем отключиться, то останется смазанное впечатление и будут вопросы с потолка. А вопросы без контекста призваны заставить вас угадывать мысли в голове задающего. Я вот в этом не силён. По мне так смысл презентации как раз в том, чтоб мои мысли стали понятны. Некоторые для этого ходят к терапевту, а некоторые на собеседования.
Времени на презентацию дали мало, особо много не спрашивали. Презентация была на английском (требование многих крупных компаний), а обсуждение уже нет. Судя по вопросам, они явно хотели в облака, но идея переходной архитектуры им зашла. Я тактично намекнул, что если б я за пару часов выдал бы тот же результат, над которым их команда работает уже не один месяц, то у них уже 3 лишних архитектора.
В любом случае, запомните главный секрет архитекторов — ответ на любой вопрос начинается с фразы «It depends». Дальше исходя из уровня английского будет либо предлог «on» и пара примеров зависимостей, либо пространное рассуждение с целью выиграть время. Но любой однозначный ответ при поверхностном знакомстве с задачей, выдаст в вас ремесленника. А нам тут криейторы нужны! Как вы, наверное, уже поняли, если дотерпели до этой точки — клепать blueprint‑ы не сложно. Сложно глядя на кубики да стрелочки увидеть потенциальные проблемы разработки, исполнения, восстановления, поддержания и противостояния инфлюенсерам, которые форсят свои серебряные пули за золотые монеты.
Стоит помнить, что положительный результат мог и не зависеть от содержания моего доклада. Может им просто срочно нужен был человек. Главное, чтоб не заставлял ждать неприятности, которых у них явно хватает. И был готов обсуждать гениальность их архитектуры с разработчиками. Сами они похоже устали. А может не совсем конкурентное предложение, которое мне сделали как раз, и основывалось на этой презентации. Я вот сам не помню, почему вдруг появилась ещё одна очередь для Callout, а не переиспользована уже существующая на диаграмме. Возможно, потому что я решил, что категоризатор не пишет ответ в репозиторий. Значит и жизненный цикл и процесс будет другой. Немного корявое исполнение диаграммы тоже оригинальное — сохранено в первозданном виде.
Если вдруг вам показалось, что этот текст как будто вырван из какой‑то большей истории:
Приглашение на собеседование и задание | |
Ответ на вопросы по легаси | |
Новый дизайн и финал |