ИТ
Фабрика
← Все статьи23 сентября 2026 г.

Мультитенантная CRM: как проверить, что данные клиентов не смешиваются

Мультитенантная CRM обслуживает нескольких клиентов на одной кодовой базе, и главный риск здесь один: данные одного клиента не должны попасть к другому. Мы закладываем изоляцию на уровне базы данных через Row-Level Security в Postgres, а не только в коде приложения, и проверяем её тестами на настоящем Postgres.

Что такое тенант и зачем изоляция на уровне базы

Тенантом называют одного клиента платформы: клуб, сеть студий или другая компания со своими филиалами, сотрудниками и клиентами. Все тенанты живут в одной схеме таблиц, а каждая строка помечена идентификатором тенанта.

Если фильтр «показывать только строки моего тенанта» стоит только в коде приложения, любая забытая проверка превращается в утечку. Row-Level Security переносит это правило в саму базу. Postgres сам отсекает чужие строки, даже если запрос написан без фильтра.

Как это устроено у нас

Схема опирается на четыре решения.

  1. У каждой таблицы есть колонка тенанта, и для неё включена политика изоляции.
  2. Связи между таблицами проверяются составным внешним ключом с тенантом. Родитель и потомок не могут принадлежать разным клиентам, и это гарантирует база, а не код.
  3. Филиал хранится отдельной таблицей, а не свободным текстом. Так не возникает расхождений вроде «Нарвская» и «нарвская».
  4. Представитель спортсмена вынесен в отдельную таблицу. Это готовит почву для семейных аккаунтов, где один родитель ведёт нескольких детей.

Как мы проверяли изоляцию

Проверка запускается на настоящем Postgres, поднятом внутри теста, а не на имитации. Тесты проходят по нескольким сценариям:

Сценарий Что ожидаем
Запрос без выбранного тенанта Пусто по всем таблицам
Переключение между тенантами Видны только строки текущего тенанта
Вставка строки чужого тенанта Отклоняется базой
Изменение чужой строки Отклоняется базой
Удаление родителя Каскад работает внутри тенанта

Что нашли по дороге

Первая версия теста проходила по неверной причине. RLS молча не действует для суперпользователя и владельца таблицы, а тест шёл именно от суперпользователя. Пришлось завести отдельную роль приложения и прогонять проверки от неё.

Вторая находка касается пула соединений. Идентификатор тенанта нужно задавать на время транзакции, иначе он «прилипает» к переиспользуемому соединению и следующий запрос увидит данные предыдущего клиента.

Частые вопросы

Достаточно ли RLS для защиты персональных данных? Нет, это один слой из нескольких. Дополнительно нужны роли доступа, журнал действий и решения по месту хранения данных. Требования 152-ФЗ мы прорабатываем отдельно, а не считаем изоляцию строк полным ответом.

Чем это лучше отдельной базы на каждого клиента? Проще обновлять и поддерживать одну кодовую базу. Отдельная база даёт более жёсткую границу и иногда нужна по требованиям заказчика. Выбор зависит от задачи и бюджета.

Можно ли переехать с одного клиента на несколько? Можно, но тенантность лучше закладывать с самого начала, потому что добавлять её в готовую систему сложнее.

Если вам нужна CRM для нескольких клиентов или филиалов, опишите задачу в форме.

Хотите обсудить своего ИИ-агента?

Опишите процесс, и мы вернёмся с оценкой сроков и бюджета, без обязательств.