Руководство Backend-разработчика Magistr
Добро пожаловать в проект Magistr! Этот бэкенд построен на Spring Boot и имеет сложную мультитенантную архитектуру, где одно приложение обслуживает множество независимых университетов, каждый со своей базой данных. В проекте также есть интеграция с Kubernetes для "горячего" управления этими тенантами.
Здесь описано, как тут всё устроено, чтобы вы ничего не сломали.
1. Архитектура мультитенантности
Мы используем подход Separate Database per Tenant (Отдельная БД для каждого клиента).
-
Как приложение понимает, к какой базе обращаться? Все запросы с фронтенда приходят с заголовком
Host(например,swsu.zuev.company). В классеTenantInterceptor(находится вconfig/tenant/TenantInterceptor.java) мы перехватываем этот запрос ДО того, как он дойдёт до контроллеров, вытаскиваем поддомен (swsu) и сохраняем его вThreadLocalпеременную через классTenantContext. -
Как переключаются базы данных? Класс
TenantRoutingDataSourceнаследуется от спринговскогоAbstractRoutingDataSource. Перед каждым запросом в базу (любойfindByIdилиsaveиз репозитория) Spring спрашивает этот класс: "Какой сейчас ключ тенанта?". Класс берёт имя изTenantContextи переключает коннект на нужную БД на лету.
Важно: Вся логика переключения абсолютно прозрачна для бизнес-кода. В контроллерах и сервисах вы пишете обычный код (
userRepository.findAll()), и он сам выполнится в нужной базе.
2. Динамическое управление тенантами (Kubernetes / ConfigMap)
Бэкенд спроектирован для работы в Kubernetes с несколькими репликами (replicas: 2+).
Список тенантов не зашит в код:
- В K8s он лежит в специальном
ConfigMap, который монтируется внутрь пода как файлtenants.json. - В классе
DatabaseControllerнаходится API для добавления нового тенанта из админки. - Чтобы изменения применились ко всем подам без перезагрузки,
DatabaseControllerвызываетConfigMapUpdater. Этот класс обращается напрямую к Kubernetes API (используя ServiceAccount токен пода) и патчитConfigMap. - В фоне работает планировщик
TenantConfigWatcher(каждые 30 секунд). Он следит за изменениямиtenants.jsonи, если видит нового тенанта, на лету поднимает для него новыйHikariCPпул соединений и добавляет в маршрутизатор баз данных.
3. Базы данных и Миграции (Flyway)
Мы НЕ используем автоматическую генерацию таблиц через Hibernate (spring.jpa.hibernate.ddl-auto=none).
Структурой баз данных правит Flyway.
Поскольку баз данных много (они создаются динамически), стандартный Spring Boot Flyway отключён. Вместо этого TenantConfigWatcher вызывает Flyway программно в момент первого подключения нового тенанта.
🛑 ПРАВИЛА ИЗМЕНЕНИЯ СТРУКТУРЫ БД:
Если вам нужно добавить новую таблицу, колонку или изменить тип поля:
-
Запрещено трогать старые файлы миграций! Запомните: файл
V1__init.sql(и любые другие V-файлы, которые уже попали в коммит) — СВЯЩЕНЕН. Если вы его измените, бэкенд не запустится на сервере с ошибкойMigration checksum mismatch. -
Как правильно добавить таблицу?
- Зайдите в папку
src/main/resources/db/migration/. - Создайте новый файл. Название строго по формату:
V<Номер>__<Описание>.sql. Например:V2__add_student_rating_table.sql. - Напишите в нём ваш SQL (
CREATE TABLE ...,ALTER TABLE ...). - Сохраните и запустите проект. Flyway сам пройдёт по всем базам данных тенантов и накатит этот скрипт.
- Зайдите в папку
-
Что если локально я накосячил в V2? Пока файл
V2_...не залит в Git и крутится только у вас на локалке, вы можете его переписывать. Но для этого вам нужно зайти в вашу локальную БД (через DBeaver/pgAdmin), вручную откатить свои кривые изменения (удалить таблицу) и удалить запись из истории Flyway:DELETE FROM flyway_schema_history WHERE version = '2';Либо, что проще: удалите контейнер с локальной БД (docker compose down -v) и поднимите заново пустую.
4. Как запускать проект локально
В корневой папке репозитория (где лежит docker-compose.yaml) поднимите инфраструктуру:
docker compose up -d
Соберется и запустится:
- Фронтенд
- Бэкенд
- Ваша локальная тестовая PostgreSQL-база данных (на порту 5432, имя базы
app_db, юзерmyuser, логин/пароль см. в compose файле).
Файл backend/tenants.json нужен для локальной разработки. Если вы запускаете бэкенд в Docker Compose, вы можете указать URL jdbc:postgresql://db:5432/app_db (где db — имя контейнера в compose сети).
Либо, если вы тестируете взаимодействие бэкенда с вашим текущим IP-адресом (например, 192.168.1.87), вы можете использовать этот IP. Оба варианта рабочие! Проект сразу подхватит настройки и накатит таблицы через Flyway.
Контроллеры и бизнес-логику пишите как в обычном Spring Boot проекте. Главное, помните: у каждого тенанта — своё изолированное хранилище!