База данных
API ChurchApps использует архитектуру одна база данных на модуль. Каждый из шести модулей данных имеет свою собственную базу данных MySQL с независимым пулом подключений, обеспечивая чёткие границы данных, сохраняя при этом всё в одном развёртывании.
Перед началом
- Установите MySQL 8.0+ — см. Требования
- Настройте строки подключения к базе данных в вашем файле
.env— см. Переменные окружения
Обзор архитектуры
Api
├── membership_db ← Люди, группы, разрешения
├── attendance_db ← Служения, сессии, записи
├── content_db ← Страницы, разделы, элементы
├── giving_db ← Пожертвования, фонды, платежи
├── messaging_db ← Разговоры, уведомления
└── doing_db ← Задачи, планы, назначения
Ключевые решения проектирования
- Одна база данных на модуль — каждый модуль поддерживает собственную базу данных MySQL с выделенным пулом подключений (управляется
KyselyPool). Это держит модули развязанными и позволяет независимой эволюции схемы. - Исключительное владение — таблицы модуля читаются и записываются только в коде этого модуля. Когда другому модулю нужны данные, он вызывает шлюз владеющего модуля, а не запрашивает таблицы напрямую — см. Кросс-модульная коммуникация.
- Шаблон репозитория без ORM — весь доступ к данным происходит через классы репозитория, которые создают типизированный SQL с построителем запросов Kysely для схемы модуля. Это обеспечивает полный контроль над производительностью и поведением запросов.
- Мультитенантность по дизайну — каждый запрос ограничен
churchId. Все таблицы включают столбецchurchId, и слой репозитория автоматически обеспечивает изоляцию тенантов.
Строки подключения
Подключение базы данных каждого модуля настраивается в .env с использованием стандартного формата строки подключения MySQL:
mysql://user:password@host:port/database
Например, локальная установка для разработки может выглядеть следующим образом:
Каждый модуль читает своё подключение из переменной окружения с названием <MODULE>_CONNECTION_STRING:
MEMBERSHIP_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_membership
ATTENDANCE_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_attendance
CONTENT_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_content
GIVING_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_giving
MESSAGING_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_messaging
DOING_CONNECTION_STRING=mysql://root:password@localhost:3306/churchapps_doing
В продакшене строки подключения хранятся в AWS SSM Parameter Store и читаются классом Environment при запуске.
Скрипты схемы
Схемы таблиц определены как миграции Kysely в каталоге tools/migrations/, организованные по модулям:
tools/migrations/
├── membership/
├── attendance/
├── content/
├── giving/
├── messaging/
└── doing/
Миграции определяют создание таблиц, индексы и изменения схемы. Каталог tools/dbScripts/ содержит данные демонстрации и семена, которые могут быть загружены поверх схемы.
Инициализация базы данных
Инициализировать все базы данных
npm run initdb
Это создаёт все шесть баз данных и запускает миграции для каждой.
Инициализировать один модуль
npm run initdb -- --module=membership
При работе с конкретным модулем вы можете переинициализировать только базу данных этого модуля, не затрагивая другие.
Шаблон доступа к данным
Репозитории создают запросы с помощью построителя запросов Kysely для типизированной схемы базы данных модуля, полученной через функцию getDb() модуля. Типичный метод репозитория выглядит следующим образом:
public async loadAll(churchId: string) {
return getDb().selectFrom("people").selectAll()
.where("churchId", "=", churchId)
.execute();
}
Репозитории получаются через RepoManager:
const repos = await RepoManager.getRepos<Repos>("membership");
const people = await repos.person.loadAll(churchId);
Всегда включайте churchId в ваши запросы, чтобы поддерживать изоляцию мультитенантов. Никогда не запрашивайте данные у нескольких тенантов, если у вас нет конкретной, авторизованной причины для этого.
Кросс-модульные ссылки
Поскольку данные каждого модуля находятся в отдельной базе данных, нет иностранных ключей или SQL-присоединений через границы модулей. Запись, которая связана с данными другого модуля, хранит id этой записи — например, пожертвование в базе данных пожертвований содержит personId человека в базе данных членства — и любая кросс-модульная композиция происходит в коде приложения.
Это ограничение — то, что делает границы модулей реальными: каждая схема может развиваться независимо, база данных модуля может быть перемещена на собственный сервер, и модуль может быть даже извлечён в автономный сервис без распутывания общих таблиц или кросс-базовых запросов.
Связанные статьи
- Структура модуля — как контроллеры и репозитории организованы в каждом модуле
- Локальная настройка API — полное пошаговое руководство по установке