Рассмотрим пример системы, в которой три сервиса используют одни и те же счета, при этом у каждого сервиса — собственная база:
- сервис показывает подробности по счёту;
- сервис считает агрегаты по клиенту;
- сервис формирует рекомендации продуктов.
Данные поступают через один сервис, который передаёт их в базы этих трёх сервисов. Источник исходных данных — внешняя система. В базах сервисов хранятся представления, подготовленные для их задач.
Нужно гарантировать, что во всех баз список счетов одинаковый. Рассмотрим три способа.
FDW
PostgreSQL Foreign Data Wrapper позволяет обращаться к удалённым таблицам и включать их в SQL-запросы. Это сохраняет данные по счетам в одном месте и позволяет выполнять джойны средствами базы.
В нашем случае запросы связывают данные нескольких сервисов. При использовании FDW результат зависит от сети и доступности каждой задействованной базы. Запросы одной системы могут создать нагрузку на базу другой, а задержки становятся менее предсказуемыми.
Вызовы API
Можно получать данные через API сервисов и объединять их в приложении. Так сохраняется владение данными: каждый сервис отвечает за свою базу и интерфейс.
Но здесь нужны джойны. Приложению придётся самостоятельно фильтровать и объединять наборы данных. Сложнее становятся сортировка и пагинация, а каждый дополнительный вызов увеличивает задержку и добавляет зависимость от другого сервиса. API удобны для небольших выборок по известным идентификаторам; для массовых соединений такой путь неудобен.
Локальные проекции
Третий вариант — передавать изменения из внешней системы в базы сервисов и хранить там локальные проекции. В каждой базе данные имеют структуру, подходящую для своего сценария чтения.
Сервис деталей читает подготовленную информацию о счёте, сервис агрегатов — данные для расчётов по клиенту, сервис рекомендаций — нужные ему признаки. Запросы не обращаются к базам соседних сервисов.
Цена — дублирование и задержка обновления. Запись в три базы не является одной атомарной транзакцией. Если обработка завершилась ошибкой посередине, одна проекция может отстать от остальных.
| Подход | Что даёт | Ограничение |
|---|---|---|
| FDW | SQL-запросы к удалённым таблицам | Связывает задержку и доступность нескольких баз |
| API | Сервисы сохраняют контроль над своими данными | Джойны и массовая обработка усложняются на стороне приложения |
| Локальные проекции | Каждая база подготовлена для своего чтения | Нужны доставка, повторы и контроль расхождений |
Что потребуется для локальных проекций
Сервис приёма должен фиксировать, какие изменения дошли до каждой базы. Повторная обработка не должна создавать дубликаты или затирать более новые данные. Ошибки доставки нужно повторять, а отставание — измерять.
Также понадобится сверка с источником и процедура восстановления проекции. Если обновления приходят не по порядку, система должна определить актуальную версию счёта и не применить старое состояние поверх нового.
Решение
Для описанного сценария выбираем локальные проекции в базах сервисов. Это позволяет каждому сервису читать подготовленные данные и не выполнять межсервисные джойны во время запроса.
Следующий шаг — определить контракт доставки: как идентифицировать версию данных, как повторять неудачную запись и как обнаруживать отставшую базу. Без этого единая точка приёма данных не гарантирует, что все три проекции будут обновлены.
