Разграничиваем Данные в Модульном Монолите. Начало
Модульные монолиты обещают производительность монолита и чёткие границы микросервисов. Каждый модуль самодостаточен: его модель предметной области, поведение и данные находятся внутри границ. Но одно из самых сложных мест для поддержания этих границ — база данных. Ничто не мешает разработчику создать JOIN-оператор между таблицами разных модулей или обойти общедоступный API.
Ранее мы рассматривали 4 уровня изоляции данных и как модули должны предоставлять явные API для доступа к своим данным. Теперь подробно рассмотрим границы в БД.
В модульном монолите каждый модуль владеет своими данными. Если модуль A обращается к таблицам модуля B, это ограничение теряется, и модули становятся тесно связанными. Вместо этого модуль B должен предоставлять общедоступный API и скрывать логику хранения своих данных от клиентов. Помимо чистого кода, соблюдение границ на уровне БД защищает вас от ошибок и упрощает последующее извлечение модуля в отдельный сервис.
Схемы БД действуют как папки: они позволяют организовывать объекты и предоставлять доступ к БД множеству пользователей, но пользователь может получить доступ к любой схеме только при наличии соответствующих привилегий. Это означает, что мы можем намеренно привязать каждый модуль к своей схеме.
Стратегия
- Создайте схему для каждого модуля и выделенную роль БД.
- Предоставьте этой роли привилегии только для её схемы и задайте для неё путь поиска по умолчанию (для PostgreSQL).
- Используйте EF с отдельными DbContext для каждого модуля, установив схему и строку подключения по умолчанию для каждого модуля.
- Для сквозных запросов создайте представление в БД, доступное только для чтения и действующее как общедоступный API.
- Необязательно: используйте политики безопасности на уровне строк для ограничения доступа внутри таблицы.
Эти методы позволят установить чёткие границы, одновременно снижая эксплуатационные издержки.
Схемы, роли и пути поиска
PostgreSQL (и многие другие БД) позволяет создавать несколько схем в одной БД. Модуль может определить свою собственную схему и роль, которой она принадлежит. Роль имеет только привилегии пользователя и уровня таблицы в этой схеме. Например, для модуля заказов:
CREATE ROLE orders_role LOGIN PASSWORD 'orders_secret';
CREATE SCHEMA orders AUTHORIZATION orders_role;
GRANT USAGE ON SCHEMA orders TO orders_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA orders TO orders_role;
ALTER ROLE orders_role SET search_path = orders;
Команда ALTER ROLE устанавливает путь поиска по умолчанию для роли, чтобы в запросах имена объектов, не имеющие схемы, разрешались в схему модуля. Если вы не хотите полагаться на путь поиска, используйте имена со схемой (orders.table_name).
Безопасность на уровне строк (Row-level security, RLS) позволяет фильтровать строки на основе выражения политики. RLS эффективен для многопользовательских сценариев или конфиденциальных данных, но он увеличивает сложность. Начните со схем и ролей и добавляйте RLS только при необходимости.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/how-to-keep-your-data-boundaries-intact-in-a-modular-monolith