🖥Database per Service. Часть 1 Начнем с довольно частой ситуации. Есть три микросервиса: Order Service Payment Service Delivery Service И одна общая PostgreSQL: shop_db

orders payments deliveries На старте это даже удобно. Order Service хочет узнать статус платежа: SELECT status FROM payments WHERE order_id = 'ORD-1001'; Payment Service хочет проверить заказ: SELECT status FROM orders WHERE id = 'ORD-1001'; Никаких дополнительных API, Kafka и событий. Просто сходили в соседнюю таблицу. Проблемы начинаются позже. Допустим, команда Payment Service решила переименовать: payments.status в: payments.payment_status Вроде небольшое изменение. Но выясняется, что к таблице payments напрямую ходят еще: Order Service Reporting Service Support Service старый batch job И изменение одной колонки внезапно требует менять несколько приложений. Формально сервисы разные. По факту: код раздельный БД общая релизы зависимые Именно эту проблему пытается решить паттерн Database per Service. Его идея простая: Каждый микросервис владеет своими данными. Например: Order Service | v order_db

Payment Service | v payment_db

Delivery Service | v delivery_db Если данные принадлежат Payment Service, Order Service не должен делать: SELECT * FROM payment_db.payments; Если ему нужен платеж, он работает через контракт Payment Service. Например: GET /api/v1/payments/by-order/ORD-1001 Или получает событие: { "eventType": "PaymentSucceeded", "orderId": "ORD-1001", "paymentId": "PAY-9001" } То есть: API или Events вместо прямого доступа к чужим таблицам. Зачем так делать? Во-первых, появляется нормальное владение данными. Сразу понятно, кто отвечает за: структуру таблиц миграции бизнес-правила качество данных изменения схемы Во-вторых, другой сервис не может обойти бизнес-логику. Представим, Payment Service запрещает переход: SUCCESS -> CREATED Если Order Service имеет прямой доступ к таблице, технически он может сделать: UPDATE payment SET status = 'CREATED' WHERE id = 100; И все проверки Payment Service просто обходятся. Когда данные закрыты за API, менять платеж можно только через правила самого Payment Service. Еще один плюс - безопасность. Order Service вообще не обязательно иметь доступ к: bank_transaction_id payer_data payment_token Если базы разделены, можно выдать каждому сервису отдельные credentials и минимальные права. Если один сервис скомпрометирован, атакующий не получает автоматически доступ ко всей БД системы. Есть и плюс по производительности. Допустим, Reporting Service случайно запустил тяжелый запрос: SELECT * FROM huge_table ORDER BY unindexed_column; На общей БД он может съесть CPU, I/O и connections так, что одновременно начнут тормозить Orders, Payments и Delivery. С отдельными хранилищами такую нагрузку проще изолировать. И еще одна важная вещь - сервисы можно масштабировать независимо. Например: Order Service -> 10 000 req/s Delivery Service -> 300 req/s У них совершенно разные требования к БД. Для order_db можно увеличить ресурсы, добавить read replica и свои индексы. delivery_db при этом вообще не трогать. Но за такую независимость приходится платить. Об этом - во второй части. #микросервисы #архитектура #системный_анализ