Как снизить коллизии в данных, когда три сервиса работают с разными БД, но используют одну и ту же бизнес-семантику.
В распределённых системах проблемы редко начинаются с чего-то громкого. Обычно всё выглядит вполне рационально: отдельные сервисы, отдельные базы, понятные границы ответственности. Но в какой-то момент оказывается, что система начинает спорить сама с собой.
У нас был кейс, где остатки пришлось разносить по трём разным базам данных.
Первая база использовалась сервисом отображения счетов. Вторая — сервисом отображения остатков по клиентам. Третья — сервисом текущих остатков по счетам. С каждой базой работал отдельный сервис, потому что у каждого контура были свои сценарии чтения, свои требования к производительности и свой формат представления данных.
На старте такое разделение казалось правильным. Проблема была в другом: всем трём сервисам нужны были одни и те же справочники.
Где именно сломалась логика
Когда одинаковые справочники живут в нескольких сервисах, система почти неизбежно начинает производить разные версии правды.
В одном месте уже обновился статус. В другом ещё осталась старая категория.
Для пользователя это выглядит как простая несогласованность интерфейсов:
- в одном экране счёт попадает в одну категорию;
- в другом агрегируется по другому признаку;
- в третьем отображается с ещё одной трактовкой.
Для команды это уже не косметическая проблема. Это инциденты, ручные сверки, долгие расследования и потеря доверия к данным.
Исходная схема
На архитектурном уровне всё выглядело примерно так:
- сервис счетов работает со своей БД;
- сервис клиентских остатков работает со своей БД;
- сервис текущих остатков по счетам работает со своей БД;
- каждый сервис использует одинаковые справочники: типы счетов, статусы, продуктовые признаки, классификаторы и другие reference-данные.
На бумаге это стандартная картина для enterprise-ландшафта. Но как только появляются несколько владельцев одной и той же семантики, начинаются вопросы, на которые уже трудно отвечать однозначно.
Кто является источником истины по справочнику? Когда обновление считается применённым? Что делать, если одна система уже живёт на новой версии, а другая ещё нет? Кто отвечает за расследование, если одно и то же поле в разных контурах интерпретируется по-разному?
Если отвечать честно, в тот момент у нас не было одного хорошего ответа на эти вопросы.
Почему справочники — это не «вторичные данные»
Это частая ловушка в проектировании. Справочники воспринимаются как нечто вспомогательное: не деньги, не транзакции, не основной поток операций. Значит, можно просто держать копию рядом с каждым сервисом и синхронизировать «как-нибудь потом».
Но на практике именно справочники определяют, как система понимает основные данные.
Остаток сам по себе — это просто число. Его смысл появляется только вместе с контекстом: тип продукта, статус счёта, принадлежность к сегменту, набор признаков для агрегации. Если этот контекст в разных сервисах отличается, то даже одинаковое значение остатка перестаёт быть одинаковым с точки зрения бизнеса.
Это уже не техническое расхождение. Это расхождение в интерпретации данных.
Какие проблемы начали проявляться
Проблемы были не в одном большом падении, а в постоянном накоплении серой зоны.
Во-первых, версии справочников расходились асинхронно. Один сервис уже применил изменения, другой ещё нет, третий обновился по отдельному процессу. В результате одна и та же сущность начинала выглядеть по-разному в разных пользовательских сценариях.
Во-вторых, заметно усложнилась диагностика. Любой инцидент в стиле «почему здесь одно, а там другое» требовал разбирать три базы, три сервиса, три истории обновления и часто ещё несколько команд. Самое неприятное — такие инциденты редко выглядят как полноценная авария. Они живут как хроническая операционная боль.
Какие варианты у нас были
Когда стало понятно, что локальное дублирование справочников начинает стоить слишком дорого, мы рассмотрели несколько вариантов.
Вариант 1. Оставить локальные копии и улучшить синхронизацию
Самый очевидный путь — ничего принципиально не менять, а просто жёстче выстроить доставку обновлений.
Плюс подхода в том, что он почти не меняет текущую архитектуру. У сервисов остаются локальные данные, быстрые чтения и привычный способ работы.
Минус в том, что проблема владения никуда не исчезает. Даже если лучше организовать синхронизацию, в системе по-прежнему остаются несколько мест, где один и тот же справочник может интерпретироваться и изменяться по-разному.
Вариант 2. Сделать общую БД для справочников
Это лучше, чем держать копии в трёх местах. Формально появляется единое хранилище.
Но такой вариант часто приводит к другой проблеме: сервисы начинают ходить в одну и ту же схему напрямую, правила начинают расползаться по коду потребителей, а контракт эволюционирует без явного владельца. В итоге единая база есть, а единое управление справочниками — нет.
Вариант 3. Выделить единый сервис справочников
Это был вариант, который мы в итоге выбрали.
Идея простая: справочники перестают быть «чем-то общим для всех», а становятся отдельным доменом с явным владельцем. Один сервис отвечает за хранение, версионирование, валидацию и публикацию изменений. Остальные сервисы больше не решают самостоятельно, какая версия справочника правильная.
Почему мы выбрали единый сервис справочников
Ключевая мысль была такой: у нас проблема не доставки данных как таковой, а множественного владения одной и той же бизнес-семантикой.
Пока справочник живёт в нескольких сервисах, система почти гарантированно будет порождать локальные трактовки:
- где-то появится дополнительный статус;
- где-то — локальный маппинг;
- где-то — особое правило фильтрации;
- где-то — временная логика, которая неожиданно станет постоянной.
Единый сервис справочников не делает систему магически простой. Но он убирает главный источник коллизий: несколько центров принятия решений по одним и тем же reference-данным.
Проще говоря, мы перестали синхронизировать три почти одинаковые правды и начали работать с одной.
Что изменилось в архитектуре
После выделения сервиса схема стала концептуально проще:
- сервис справочников стал владельцем canonical-модели;
- сервис счетов, сервис клиентских остатков и сервис текущих остатков перестали самостоятельно владеть справочниками;
- правила валидации, версионирования и обновления были централизованы;
- все коллизии начали разбираться относительно одного источника истины, а не трёх локальных копий.
Что должен уметь полноценный сервис справочников
Если делать такой сервис всерьёз, а не как тонкую CRUD-обёртку поверх таблицы, у него есть несколько обязательных обязанностей.
Во-первых, он должен быть владельцем модели. Не просто хранить данные, а определять поля, статусы, связи, ограничения и правила жизненного цикла.
Во-вторых, он должен поддерживать версионирование. Потребитель должен в любой момент уметь ответить на вопрос, какую версию справочника он использует.
В-третьих, он должен предсказуемо публиковать изменения. Это может быть API, события, снапшоты или гибридный подход — главное, чтобы схема доставки была единой и контролируемой.
В-четвёртых, он должен обеспечивать контроль качества данных: валидации, аудит, дедупликацию, проверку обязательных полей и прозрачный процесс изменений.
В-пятых, он должен быть наблюдаемым. Если от справочников зависит корректная интерпретация остатков, то нужны метрики свежести, версий, ошибок обновления и отставания потребителей.
Trade-offs
| Подход | Плюсы | Минусы |
|---|---|---|
| Локальные копии справочников в каждом сервисе | Быстрые локальные чтения, минимальная зависимость на сетевые вызовы, автономность сервисов | Дрейф данных, сложное расследование, несколько источников истины |
| Общая БД справочников | Формально единое хранилище, относительно простой старт | Сильная связность по схеме, риск обхода правил, нет явного владельца контракта |
| Единый сервис справочников | Централизованное владение, прозрачные правила, меньше коллизий, проще аудит и контроль версий | Новый критичный компонент, выше требования к SLA, нужно отдельно проектировать модель распространения изменений |
Мы выбрали третий вариант, потому что в нашем случае главной стоимостью была не задержка чтения, а цена несогласованности данных между контурами.
Где легко ошибиться
Самая частая ошибка — назвать сервисом справочников обычную БД с REST-обёрткой.
Если у такого сервиса нет владения моделью, нет версий, нет контроля изменений и нет прозрачной схемы распространения данных, то он не решает исходную проблему. Он просто переносит её в другое место.
Вторая ошибка — заставить все сервисы читать справочники только синхронно и только в онлайне. Это быстро превращает инфраструктурный сервис в точку каскадной деградации.
На практике почти всегда нужен гибридный подход:
- единый сервис владеет reference-данными;
- изменения публикуются централизованно;
- потребители могут держать локальные read-optimized представления там, где это оправдано по нагрузке и latency;
- право менять и интерпретировать справочник остаётся в одном месте.
Decision log
Контекст. Три сервиса работают с тремя разными БД, но используют одни и те же справочники для интерпретации остатков.
Проблема. Возникают коллизии, расхождения версий и сложные инциденты, где разные контуры показывают разные значения или категории для одной и той же сущности.
Варианты. Оставить локальные копии и усилить синхронизацию; сделать общую БД; выделить единый сервис справочников.
Выбор. Выделить отдельный сервис справочников как единого владельца reference-данных.
Почему. Проблема была не только в доставке данных, а в множественном владении одной и той же бизнес-семантикой.
Последствия. Меньше коллизий, проще расследование, более прозрачная эволюция справочников. Цена — появление нового критичного сервиса, который нужно проектировать как часть core-архитектуры.
Что я бы отдельно заложил в такую систему
Если проектировать подобную схему сразу под production, я бы обязательно закладывал следующие вещи:
- один writer для справочников;
- явные версии и timestamp публикации;
- контроль freshness у потребителей;
- аудит изменений и понятный ownership;
- backward compatibility для изменений в контракте;
- наблюдаемость не только по доступности сервиса, но и по актуальности данных у потребителей.
Вывод
Когда несколько сервисов используют одни и те же справочники, вопрос довольно быстро перестаёт быть вопросом хранения таблиц. Это становится вопросом владения смыслом данных.
В нашем случае было три базы, три сервиса и одна общая проблема: одинаковые справочники жили в нескольких местах и со временем начинали расходиться. Выделение единого сервиса справочников не убрало всю сложность системы, но убрало самый токсичный её вид — конкурирующие версии правды.
Если коротко, то вывод такой: в распределённой системе можно жить с разными БД, разными сервисами и разными read-моделями. Но если справочники определяют, как бизнес понимает данные, у них должен быть один хозяин.
Top comments (0)