
Раньше наш способ собрать данные из всех микросервисов в одно место (в витрину) напоминал копролит (только не древний, а наш собственный ИТ-артефакт). Он был сложный, медленный, постоянно ломался и требовал много ручной работы. Данные размазывались по куче баз данных. Чтобы сделать отчёт или отправить данные во внешнюю систему, надо было собирать их вместе.
Высока вероятность, что у вас такой же или похожий. Если нет — приходите рассказать «а я же говорил» в комментариях.
Мы тратили время на латание дыр, а не на разработку. В какой-то момент решили, что пора выкинуть этот велосипед и внедрить нормальный, промышленный подход к работе с данными — Data Lakehouse с медальонной архитектурой.
В чём были наши ошибки:
Спроектировали сложную доменную модель для витрины, которая не соответствовала ни моделям в исходных сервисах, ни требованиям пользователей. Одна таблица из микросервиса могла раскладываться на 5 таблиц в витрине.
Превращали данные из сервисов в эту модель — это был ад.
Схема данных на фронте, в сервисе, в витрине и у потребителя была везде разная. Это постоянные баги из-за того, что кто-то ждёт ID, а ему приходит business_number.
Чтобы отдать данные потребителю, приходилось делать кучу JOIN-ов. Это ломало SLA по производительности.
Любое изменение в схеме БД микросервиса (добавил колонку) требовало сложной доработки витрины. Всё тесно связано и хрупко.
Сервисы писали изменения в свои локальные outbox-таблицы, а отдельный обработчик забирал их и складывал в витрину. Данные из разных сервисов приходили в разное время. Запрос к витрине мог пытаться сджойнить данные о клиенте и его заказе, но данные по заказу ещё не приехали. В итоге потребителю уходило неполное или вообще никакое сообщение.
Разработчики сервисов вместо одного события «Заказ сохранён» генерировали 20 событий на каждое изменение поля в UI. Это забивало очередь и создавало дикую нагрузку.
Тестирование — тоже ад.
Для таких ситуаций есть понятный шаблон — трёхслойная архитектура с понятным дата-пайплайном.
Почему прошлая схема зашла в тупик
Всё началось с того, что мы решили построить идеальную универсальную модель данных. Зарылись в проектирование сложного DDD, которое в итоге не имело ничего общего с реальностью.
Эта модель не совпадала ни с базами данных исходных микросервисов, ни с тем, что хотят видеть конечные пользователи в отчётах. В итоге одна простая таблица из сервиса-источника при переезде в витрину разлеталась на пять разных таблиц. Инженерам приходилось городить сложную логику, чтобы просто разложить данные по местам.
Постоянная головная боль — путаница с названиями полей. Фронтенд называет поле одним именем, микросервис другим, витрина третьим, а потребитель ждёт четвёртое. В итоге вылезали ошибки, когда потребитель ожидает технический ID, а получает business_number. Каждое такое расхождение приходилось вылавливать и править руками.
Кроме того, в разных сервисах иногда совпадали названия таблиц и полей и приходилось тратить время на дополнительную аналитику по выбору правильного поля для отправки потребителям, а потом в случае ошибки сложно распутать клубок, откуда были загружены данные.
Технически это работало так: микросервис записывает изменения в свою локальную outbox-таблицу. Специальный обработчик постоянно опрашивает эти таблицы и перекладывает данные в витрину. Из-за того что сервисы независимы, данные доезжают в разное время. Когда витрина пытается собрать сообщение для внешней системы через джойн (например, склеить «Клиента» и его «Заказ»), часто оказывается, что клиент уже приехал, а заказ ещё в пути. В итоге потребитель не получал нужную ему информацию.

Система в целом получилась максимально хрупкой. Если разработчик в микросервисе просто добавлял одну колонку в базу, то всё ломалось по цепочке до самой витрины. Всё связано слишком жёстко. Вдобавок к этому шину данных забивали мусором. Вместо того чтобы отправить одно событие «Документ сохранён», сервис выплёвывал по 20 событий на каждый чих пользователя в интерфейсе. В итоге скорость доставки падала, очереди на шине росли.
Переход к Data Lakehouse и медальонной архитектуре
Теперь вместо того чтобы сразу впихивать данные в жёсткую модель, используется Data Lakehouse и три слоя обработки.
В первом слое (бронзовый) данные сохраняются ровно в том виде, в котором они лежат в микросервисах. Никто ничего не переименовывает и не трансформирует. Вместо того чтобы постоянно дёргать базы данных через Polling, данные залетают через Kafka. Это намного быстрее и надёжнее.
В бронзовом слое накапливаются данные с возможностью проверки, отслеживания истории происхождения, их можно многократно обрабатывать без необходимости повторного считывания из источников.

Второй слой — серебряный. Здесь данные чистят и приводят к порядку. Тут как раз решается проблема с именованием: все ID и business_numbers приводятся к единому стандарту. Данные всё еще детальные, но уже понятные всей системе. Серебряный слой используют дата-аналитики для глубоких исследований и дата-сайентисты для построения моделей.
Ну и третий золотой слой — это финальные витрины под конкретные задачи. Главная фишка в том, что структура каждой витрины теперь полностью совпадает с тем, что хочет видеть конкретный потребитель. Данные здесь лежат в денормализованном виде (плоские таблицы). Больше не нужно делать тяжёлые джойны, когда кто-то запрашивает отчёт, поэтому всё работает быстро и укладывается в SLA.
Как это решает проблему консистентности
Главное изменение — логика отправки данных. Теперь витрина не пытается собрать сообщение на лету из того, что успело доехать. Сначала всё накапливается в Data Lake. Только когда система видит, что приехал полный и согласованный набор данных от всех нужных микросервисов, она формирует итоговое сообщение.
Мы выделили три независимых потока обработки: миграции, интеграции и инициирующие выгрузки. Теперь важные сообщения не стоят в пробке за тяжёлыми отчётами.
Чтобы не переписывать код под каждого нового клиента, сделали отдельный функционал для управления выгрузками. Теперь подключение нового потребителя — это вопрос конфигурации, а не разработки с нуля.
Что получили на выходе и почему решение нам так нравится
Когда перестали латать старый велосипед и внедрили новое решение, числа сразу пошли вверх. Сейчас система обслуживает больше 3000 табельных номеров и работает стабильно.
Ниже новая схема архитектуры. Изменения на ней, по сравнению с прошлой версией, выделены зелёным.

Что нового и почему это круто:
Внедрили Kafka для загрузки данных на основании outbox-таблиц из сервисов в витрину и далее для отправки из витрины на шину (маршрутизатор) к конечным потребителям.
Добавили новый слой Data Lake, чтобы можно было ждать пока доедут все данные и без ошибок собирать витрину для потока сообщений, восстанавливать состояние (реконсиляция) на дату, делать инициирующие выгрузки, не опасаясь, что базы данных сервисов что-то удалят.
Ушли от доменной модели в витрине и сделали её под себя, без нормализации и лишних таблиц.
Добавили компонент настройки потоков, который позволяет:
o Легко подключать нового потребителя за счёт редактирования настроек, но не разработки нового кода, и это значительно упростило нам масштабирование. Например, подключение двух новых потребителей теперь занимает три месяца вместо бесконечного цикла доработок сложной доменной модели и последующей отладки отправляемых сообщений. Главное — собрать требования, а сам процесс подключения уже отлажен.
o Делать настраиваемую параллельную обработку потоков для потребителей и задавать очерёдность отправки сообщений, где это требуется потребителю.
o Контролировать отправку сообщений и ставить отправку на паузу, если потребитель временно недоступен и не принимает сообщения.
o Задавать режимы отправки сообщений для потребителя: нормальный режим работы, инициирующая выгрузка, реконсиляция через панель администратора с дружественным интерфейсом, а не запуском скриптов.
Добавили внутреннюю сверку данных, чтобы гарантировать потребителям, что всё, что есть в источнике, было отправлено в сообщениях. Благодаря этой сверке мы оперативно отслеживаем ошибки из-за новой бизнес‐логики (которую не предусмотрели) или аппаратных сбоев, и оперативно их устраняем.
Ещё проработали заполнение слоя метаданных, и теперь пользователи формируют гибкие отчёты с помощью интуитивно понятного интерфейса, а также предусмотрели отдельную витрину для отчётов.
