СамоСМиИ-2024-АК. Архитектурные решения. Каждой (твари)сервису по базе

Написан пост с описанием какой-то практики вашей работы с использованием формата архитектурного паттерна (в нашем курсе приведено 13 возможных разделов, использовано не менее 8 из них).

Довольно распространенный паттерн в микросервисных архитектурах

имя паттерна

  • database per service

ситуация/context

  • микросервисная архитектура, слабая связность между модулями

проблема

  • выбор подхода ко владению БД в микросервисной архитектуре

соображения/влияния/силы/forces

  • микросервисы должны быть слабосвязанными
  • некоторые бизнесс-транзакции запрашивают/joined данные которыми владеют разные домены/сервисы
  • некоторые бизнес-транзакции меняют данные нескольких доменов/сервисов
  • базы данных иногда должны быть реплицированы/шардированы
  • разные сервисы следуют разным паттернам доступа/хранения данных SQL/NoSQL/Graph/Document/etc.

решение

  • держать приватное хранилище данных исключительно во владении микросервиса и предоставлять API для доступа к данным(не давать прямого доступа к БД)
  • есть вариации:
    • приватные таблицы на каждый сервис - общая БД/кластер, но таблицы находятся в исключительном доступе/владении у каждого сервиса
      • накладные расходы меньше, но есть опасность, что какой-то шибко умный/“очень надо” разраб на это наплюет. Нужно будет вложиться в проверки чтобы этого не было
    • приватная БД/кластер на каждый сервис

последствия

  • плюсы
    • слабая связность
    • каждый сервис может использовать что угодно свое для хранения. То что подходит лучше
  • минусы
    • бизнесс-транзакции между разными доменами это боль
    • мерж данных из разных источников не такой простой как один SQL запрос
    • сложность/стоимость менеджмента/обслуживания разных БД

известные использования

добавляют часто также варианты паттерна

  • SAGA - для распределенных eventual-consistent транзакций

UPDATE:

имя паттерна

  • database per service

ситуация/context

  • есть сервис А и сервис Б
  • сервис А появился первым, у него был какой-то API и всякие фронтенды(но не бэкенды) им пользовались
  • важный момент - в сервисе А модель работы была append-only, т.е. данные только создавались, но никогда не менялись/удалялись(это было нашей большой ошибкой так сделать) и до поры до времени это не было проблемой
  • потом понадобился сервис Б, который делает какую-то аггреграцию данных из А и сверху наворачивает данных
  • и поскольку сервис А работал по append-only - то в сервисе Б чтобы сократить количество API вызовов между сервисами - делали локальный кэш из трансформированных данных из А
  • в сервис А добавляли данные, он слал эвенты в сервис Б, сервис Б брал эти обновления, наворачивал свое, трансмформировал, кэшировал и все было хорошо и eventual-consistent со средним отставанием в ~1-2 секунды

проблема

  • появился use-case когда данные в сервисе А нужно было менять, а посколько Б зависит от А то и в Б по цепочке их нужно менять
  • при этом время между append - update может пройти часы/дни, а могут миллисекунды(т.е. две “типа-транзакции” одновременно выполняться)

соображения/влияния/силы/forces

  • микросервисом А все еще пользуются другие фронтенды(но не бэкенды)
  • все еще eventual-consistency нас устраивает(отставание в районе 1-2 минут приемлимо)
  • в целом у меня полная свобода что делать но времени не очень много

решение

  • когда что-то меняется в А - слать эвент в Б, а Б делает полную рекалькуляцию без локального кэша

последствия

  • плюсы
    • консистентно
    • новый use-case будет поддержан
  • минусы
    • больше вызовов между сервисами
    • больше latency

известные использования

добавляют часто также варианты паттерна

  • SAGA - для распределенных eventual-consistent транзакций
    • решили не делать, т.к. муторно, сложно и менее надежно

Интересный, ретроспективный текст получился. Эти изменения еще не внедрили, в процессе

1 лайк

У вас это как-то похоже не на описание вашей работы в предложенном формате, а пересказ содержания учебника по архитектуре, “фрагмент сборника общих паттернов”, независимо от конкретного вашего проекта. Никаких сомнений, что паттерн “из учебника” известен, может быть описан в этом формате. А надо бы заземлить: как это выглядит в конкретно вашей ситуации. Какие там конкретно боли, какие конкретно решения (понятно, как-то обезличенно, но тут вроде и разговор не про людей).

1 лайк

Боли на эту тему у меня как раз в соседнем посте)

1 лайк

У меня препод говорит “сага это конечные автоматы для бедных”) И дело не только в том, что у нас в микросервисном аду данные запаздывают. А ещё и в том, что правильные откаты транзакций это ж целое дело. И надо разбираться какая транзакция “поворотная”, а какая нет. И каком порядке их выстроить. А потом вдруг откатывать надо данные не в одном сервисе, а в нескольких по какой-нибудь хитрой цепочке.

1 лайк

Да, это вот примерно так и выглядит, если от уровня рассуждений “паттерна из учебника” (метаУ-модели) перейти к конкретной ситуации в предметной области, конкретным экземплярам микросервисов. Ибо паттерн, предъявленный без контекста – а вдруг нужен был вообще другой паттерн?!

1 лайк

если честно звучит как наброс. Конечно можно представить SAGA как частный случай конечного автомата. Ну так чего останавливаться, давай те скажем, что SAGA - частный случай алгоритмов.

Все так, это очень непросто и лучше в это вообще не залезать))

Полезно очень заземление провести оказалось. Пока писал еще раз подумал, а в нужную ли сторону вообще идем

2 лайка

То, что указано в “проблема” - это проблема конкретного человека (не выбран подход), но не та проблема, которая решается этим паттерном.
В целом database per service - это энкапсуляция.
Одна из проблем, котороая решается этим паттерном - это бесконтрольный доступ одного сервиса к данным другого сервиса, нарушение бизнес-правил/инвариантов.
Эта проблема может решаться через логику в БД через хранимые процедуры, что в целом считается плохим паттерном по другим причинам.
Другая альтернатива - доступ через explicit API с возможностью контролировать все запросы и то, как они работают с БД.
Другая проблема - это изменения внутренней структуры БД и возможность экранировать внешние сервисы от этих изменений с помощью явного использования API, которое сервис предлагает.

То, что написано в секции UPDATE - имя паттерна не имеет отношения к описанному решению. Можно сказать, что переход к использованию этого паттерна стало причиной проблемы, по сравнению с тем, что было раньше (при прямой записи в БД).

дельное замечание, согласен, да. Этот паттерн был призван решить какую-то другую проблему, решил ее(скорее да, чем нет), а теперь породил или был одной из причин новой проблемы - проблема распределенных транзакций.