Синхронизация ролевой модели между окружениями: как мы избавились от «работает только на проде»

На проекте было три окружения: тест, препрод и прод. Ролевую модель вели поддержка и менеджеры только на проде — остальные окружения оставались с неполной моделью. В итоге повторить некоторые сценарии на тесте или препроде было невозможно. Мы построили систему синхронизации: добавляем и удаляем сущности, ставим флаги игнорирования, синхронизируем только из прода, запускаем по cron и по кнопке из админки, сверяем по slug. Ролевая модель теперь успешно синхронизируется между окружениями.

Проблема: «работает только на проде»

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

Но у этой системы была одна особенность, которая со временем стала проблемой.

Как это работало:

  • На проекте было три окружения: тест, препрод и прод.

  • Ролевую модель вели поддержка и менеджеры только на прод окружении.

  • Тест и препрод получали неполную ролевую модель — после того как её дополняли на проде.

Что это давало на практике:

  • Повторить некоторые сценарии из продакшена на тесте или препроде было невозможно.

  • Причины — различия окружений: данные на проде отличались от тестовых.

  • Тестирование ролевой модели превращалось в лотерею: «а как оно там, на проде?»

Классическая ситуация: «У меня локально работает, а на проде — нет». Только в масштабе ролевой модели целого проекта.


Решение: синхронизация ролевой модели

Мы провели анализ и построили систему синхронизации. Вот из чего она состоит.

1. Синхронизируем и удаления, и добавления

Синхронизация должна отражать полное состояние ролевой модели: если на проде роль добавлена — она появляется на тесте и препроде. Если удалена — удаляется.

Это важно, потому что иначе окружения накапливают «мусор»: сущности, которых на проде уже нет.

2. Флаги игнорирования для ролей, доступов и групп

Для сущностей ролей, доступов и групп мы поставили параметр для игнорирования — как добавления, так и удаления.

Зачем это нужно:

  • Не удалить нужные сущности при синхронизации. Например, на тесте есть сущность, которая нужна для тестирования, но на проде её нет. Флаг игнорирования защищает её от удаления.

  • Не добавить лишних сущностей. Если сущность помечена как игнорируемая, она не будет синхронизирована.

Это гибкий механизм: можно точечно управлять тем, что синхронизируется, а что остаётся локальной особенностью окружения.

3. Тест и препрод синхронизируются только из прода. Прод синхронизироваться не может из другой среды.

Направление синхронизации — одностороннее: из прода в тест и препрод.

Почему так:

  • Прод — источник правды для ролевой модели.

  • Тест и препрод — получатели.

  • Обратная синхронизация (из теста в прод) создала бы риск: тестовые изменения могли бы попасть в продакшен.

4. Синхронизация: cron + кнопка в админке

Мы реализовали два способа запуска синхронизации:

  • Фоново по расписанию: планировщик cron + schedule Laravel. Запуск каждый час в рабочее время — с 09:00 по 18:00.

  • По кнопке из админ-панели: кнопка присутствует только на тест и препрод окружениях, регулируется конфигурацией.

Почему оба способа:

  • Cron — для регулярной синхронизации без участия человека.

  • Кнопка — для случаев, когда нужно синхронизировать «здесь и сейчас»: например, после срочного изменения на проде.

5. Сверка данных по slug

Сверка данных идёт по slug, а не по UUID.

Почему:

  • UUID уникален, но по нему нет возможности искать записи — он не предназначен для поиска.

  • Slug — человекочитаемый идентификатор, по которому можно сопоставить сущности между окружениями.

6. Защита API: белый список IP и доступы API-пользователя

API-метод прода имел ограничение по белому списку IP-адресов. Кроме того, проверялись доступы API-пользователя: не все API-пользователи имели доступ к данному методу.

Зачем:

  • Белый список IP — защита от несанкционированных вызовов извне.

  • Доступы API-пользователя — дополнительный уровень контроля: только уполномоченные пользователи могут запрашивать данные ролевой модели.

7. Отдельные пользователи для теста и прода с bearer-токенами

Для теста и прода мы завели отдельных пользователей, которые работали по bearer-токену со сроком жизни сутки.

Почему:

  • Отдельные пользователи — изоляция: если токен теста скомпрометирован, это не влияет на прод.

  • Bearer-токен со сроком жизни сутки — баланс между безопасностью и удобством: токен не нужно передавать при каждом запросе, но он автоматически истекает.


Как это работает: полный цикл

Опишем, как происходит синхронизация.

  1. На проде поддержка или менеджер добавляет новую роль, доступ или группу.

  2. Cron (или кнопка в админке) запускает синхронизацию на тесте и препроде.

  3. Запрос уходит на API прода — с bearer-токеном, с IP из белого списка.

  4. API прода проверяет доступы API-пользователя и возвращает данные ролевой модели.

  5. Тест и препрод сверяют данные по slug — определяют, что добавить, что удалить.

  6. Учитываются флаги игнорирования — сущности с флагом не синхронизируются.

  7. Изменения применяются — ролевая модель на тесте и препроде обновляется.


Результат: синхронизация работает

Мы реализовали всё из списка — и синхронизация сущностей ролевой модели успешно работает между окружениями.


Стек

  • Backend: Laravel (PHP)

  • Планировщик: cron + Laravel Schedule

  • API: bearer-токены, белый список IP

  • Сверка данных: по slug


Что мы вынесли из этой истории

1. Окружения должны быть сопоставимы

Если тест и препрод не отражают прод — тестирование теряет смысл. Синхронизация ролевой модели сделала окружения сопоставимыми.

2. Односторонняя синхронизация — безопаснее

Синхронизация только из прода в тест и препрод исключает риск, что тестовые изменения попадут в продакшен.

3. Флаги игнорирования — гибкость без хаоса

Не всё нужно синхронизировать. Флаги игнорирования позволяют сохранить локальные особенности окружений, не ломая общую картину.

4. Безопасность API — обязательна

Белый список IP и bearer-токены с ограниченным сроком жизни — минимальные меры для API, который отдаёт данные ролевой модели.


Заключение

Эта история — про то, что ролевая модель — это не только интерфейс и сетка доступов. Это ещё и инфраструктура: синхронизация между окружениями, защита API, механизмы сверки.

Мы построили систему, которая избавила команду от «работает только на проде». Теперь тест и препрод отражают реальную ролевую модель — и тестирование стало предсказуемым.

Обсудим проект

Расскажите о задаче — оценим за пару дней

Контакты