
Многоклиентская SaaS-архитектура на NestJS: практическое руководство по надёжной эксплуатации
- NestJS multi tenant architecture
- nestjs
- architecture
- development

Многоклиентность — это модель доверия, которая влияет на каждый путь выполнения в SaaS-продукте. Одного идентификатора клиентской организации в таблице базы данных недостаточно. Клиентский контекст нужно получать на доверенной границе и сохранять в запросах HTTP и GraphQL, соединениях WebSocket, очередях, плановых задачах, кешах, файлах, поиске, аналитике, журналах, расчётах, экспорте, средствах поддержки, резервных копиях и процедурах восстановления. Достаточно упустить один путь — и система может открыть данные другого клиента или применить неверное коммерческое правило.
Начните с фактов о продукте и рисках: кого именно представляет клиентская организация; может ли пользователь состоять в нескольких организациях; какие ресурсы, учётные записи, тарифные планы, ключи, регионы и сроки хранения различаются; какой изоляции требуют законы и договоры; как распределены данные и нагрузка между крупнейшими клиентами; насколько нужны индивидуальные настройки; насколько система должна выдерживать чрезмерную нагрузку от одного клиента; какие действуют требования к восстановлению и удалению; кто будет эксплуатировать платформу. Только после этого выбирайте границы данных и вычислительных ресурсов. Механизм внедрения зависимостей NestJS способен передавать службы с ограниченной областью действия, но сама область действия приложения ещё не создаёт доверия.
В этом руководстве общая, распределённая, изолированная и смешанная модели рассматриваются как осознанный выбор между преимуществами и ограничениями. Архитектура рабочей системы должна объяснять, как выполняются идентификация, проверка прав, доступ к данным, фоновая обработка, настройка, расчёты, миграции, наблюдаемость, реагирование на инциденты и переход между уровнями изоляции. Лучшая модель — самая простая из тех, что удовлетворяют реальные требования к изоляции и эксплуатации и допускают надёжную проверку.
Выбирайте модель многоклиентности с учётом требований к изоляции и эксплуатации
Общее приложение с общей схемой данных обычно требует наименьших затрат на инфраструктуру и миграции. Каждая принадлежащая клиенту строка содержит неизменный ключ клиентской организации; его учитывают все ограничения уникальности, индексы, связи и запросы. Такая модель подходит многим продуктам, если логической изоляции достаточно, а средства защиты трудно обойти. Её главный риск — большой масштаб последствий из-за пропущенного условия в запросе, небезопасного ключа кеша или административного запроса без ограничения по клиенту.
Общие вычислительные ресурсы с отдельными схемами или базами данных усиливают изоляцию, дают больше возможностей для резервного копирования и восстановления отдельного клиента и упрощают индивидуальную настройку. Вместе с тем они усложняют управление соединениями, миграции схем, мониторинг, планирование мощностей и сопровождение всей совокупности экземпляров. Полностью выделенные комплексы приложения и инфраструктуры способны обеспечить самые строгие регуляторные, региональные, криптографические, границы по производительности или договорным требованиям, но отличаются наибольшей стоимостью эксплуатации. В смешанной модели большинство клиентов может работать на общем уровне, а отдельные — в изолированных ячейках.
Проверяйте выбор на конкретных ситуациях: чтение данных другого клиента, привилегированный доступ поддержки, исчерпание ресурсов крупным клиентом, региональные ограничения, восстановление отдельной организации, разделение ключей шифрования, развёртывание новой схемы в тысячах баз данных, экспорт и удаление данных, переход из общей среды в изолированную. Зафиксируйте, где именно действует каждая граница — в коде приложения, правилах базы данных, учётной записи или облачной подписке, сети, ключе либо отдельном развёртывании — и кто проверяет её действенность.
Общее, распределённое или изолированное хранение данных
Общие данные требуют составных ключей и ограничений. Первичные и внешние ключи, естественная уникальность, индексы и обработка конфликтов должны в соответствующих местах охватывать границу клиентской организации: например, адрес электронной почты или внешний идентификатор могут быть уникальными внутри одного клиента, а не всей системы. Интерфейсы хранилищ явно принимают клиентский контекст и возвращают типы, ограниченные этим клиентом. Этому же правилу следуют прямые запросы, отчётность, импорт, служебные сценарии и миграции.
Правила доступа к строкам в базе данных могут стать дополнительным эшелоном защиты, если их поддерживают выбранная база и модель соединений. Однако это работает лишь тогда, когда контекст задаётся внутри транзакции и гарантированно очищается. Из-за пулов соединений неудалённое состояние сеанса особенно опасно. Для отдельных баз данных нужны реестр, безопасное получение учётных данных, ограниченные пулы соединений, контроль состояния и версий, согласованное выполнение миграций, правила резервного копирования и защита от запроса, способного создать неограниченное число соединений.
Проверяйте изоляцию как неизменное свойство системы. Создавайте записи с одинаковыми идентификаторами для разных клиентов; проверяйте чтение, изменение, удаление, агрегирование, поиск, разбиение на страницы, связи, кеши, фоновые задачи, экспорт, журналы и административные операции; отдельно испытывайте отказ в доступе и одновременное выполнение. Для резервных копий проводите настоящее восстановление и сверку данных на обещанной границе. Индивидуальная разработка на NestJS поможет превратить выбранную модель в последовательные этапы внедрения и миграции с чёткими границами.
Создавайте клиентский контекст на доверенной границе
Идентификатор клиентской организации может поступать из проверенного собственного домена, поддомена, маршрута, выбранной организации, учётных данных API или подтверждённого подписью свойства токена. Считайте имя узла, заголовки и значения маршрута лишь подсказками, пока не сопоставите их с аутентифицированным членством и действующей организацией в доверенном реестре. Не позволяйте вызывающей стороне произвольно задавать идентификатор клиента, который последующий код примет без проверки.
После аутентификации и проверки членства создайте единый неизменный контекст запроса. Он может содержать идентификатор организации, пользователя или служебную учётную запись, сведения о членстве, идентификаторы запроса и цепочки операций, важные для правил доступа признаки и подтверждение временного доступа поддержки от имени пользователя. Передавайте его через явную границу прикладного уровня или тщательно контролируемый контекст асинхронного выполнения. Не заставляйте NestJS создавать весь граф зависимостей заново для каждого запроса, если это лишь увеличивает расход памяти и сложность проверки, не усиливая изоляцию.
На каждой границе контекст нужно устанавливать заново. Сообщения в очереди и плановые задачи должны содержать доверенную или подписанную ссылку на клиентскую организацию и неизменные идентификаторы хозяйственной операции; обработчики проверяют их и не наследуют неявное состояние HTTP-запроса. Соединения WebSocket повторно проверяют членство после изменения сеанса или ролей. Журналы, трассировка, метрики, ключи кеша, пути к файлам, поисковые индексы и исходящие запросы должны безопасно помечаться клиентской организацией, не раскрывая чувствительных значений.
Разделяйте аутентификацию и проверку прав внутри организации
Аутентификация подтверждает субъекта доступа; проверка прав определяет, может ли этот субъект именно сейчас выполнить конкретное действие с конкретным ресурсом в конкретной клиентской организации. Действующего токена с идентификатором организации недостаточно. На серверной границе проверяйте издателя и назначение токена, подпись, срок действия, отзыв или правила сеанса, состояние членства, выбранную организацию, роль либо другие признаки, доступные по плану возможности, принадлежность ресурса и условия конкретного действия.
Держите правила доступа рядом с предметными операциями. Защитные компоненты NestJS могут отклонить неаутентифицированный запрос и задать общие требования для маршрута. Однако проверке прав на конкретный ресурс часто требуется уже загруженное состояние, поэтому её место — в службе правил доступа или обработчике команды. Исходным решением должен быть отказ в доступе. Не разбрасывайте строковые названия ролей по коду и не ограничивайте проверки контроллерами: фоновые задачи, обработчики GraphQL, внутренние вызовы или новые протоколы могут их обойти.
Явно опишите приглашения, изменения членства, прекращение доступа, служебные учётные записи, ключи API, роли администраторов и аварийный доступ поддержки. Для привилегированной работы от имени пользователя нужны согласование, указанная цель, ограничение времени, заметное предупреждение, защищённый от незаметной подмены журнал аудита и способ работы без раскрытия учётных данных. Проверяйте горизонтальное и вертикальное повышение привилегий, устаревшие токены, удалённое членство, переключение между организациями, угадывание идентификаторов, массовые операции и экспорт.
Сделайте изоляцию данных стандартным путём
Изоляцию должно быть трудно забыть. Размещайте клиентские хранилища и средства доступа к данным за интерфейсами, которые обязательно требуют контекста; формируйте ключи кеша и защиты от повторного выполнения операций только утверждёнными средствами; централизованно создавайте пространства имён для файлов и поиска; применяйте статический анализ и архитектурные проверки, чтобы запретить опасные пути доступа. Избегайте глобальной переменной «текущий клиент» и необязательных параметров клиента, отсутствие которых расширяет запрос.
Аналитика и эксплуатационные операции требуют такой же дисциплины. Сводная отчётность по всем клиентам должна иметь отдельный разрешённый путь, определённую цель, собственную модель данных и журнал аудита, а не отключать обычные ограничения. Не записывайте в журналы содержимое запросов и секреты; клиентские признаки должны соответствовать правилам конфиденциальности и допустимого числа значений. В средах вне рабочей системы используйте искусственные или должным образом защищённые данные. Экспорт и удаление должны охватывать производные хранилища, файлы, поиск, аналитику, очереди, резервные копии и копии поставщиков в соответствии с согласованным жизненным циклом.
Исходите из того, что какой-либо защитный механизм рано или поздно откажет, и стройте несколько уровней: ограничения в приложении, правила или ограничения базы данных, отдельные учётные данные, ключи шифрования там, где они нужны, сетевые границы и границы учётных записей, обнаружение аномалий и доказательства для аудита. Инструкция по реагированию на инцидент должна помогать определить пострадавших клиентов, локализовать доступ, сохранить доказательства, выполнить обязанности по уведомлению, сверить данные и подтвердить восстановление, не расширяя утечку.
Передавайте сведения о клиенте в фоновые задачи, расчёты и настройки
Фоновые задачи — это операции клиента, которые выполняются без HTTP-запроса. Передавайте в очередь ссылку на клиентскую организацию, неизменный идентификатор операции, сведения об исполнителе или системную цель и данные сообщения с явно указанной версией; при получении проверяйте контекст; делайте последствия повторного выполнения безопасными; ограничивайте повторные попытки; отделяйте сообщения, которые не удалось обработать; сохраняйте связь между этапами операции. Справедливое планирование, ограничение параллельности для каждого клиента, квоты и регулирование нагрузки не позволят импорту или отчёту одного клиента лишить ресурсов всю платформу.
Разделяйте тарифные планы, права по тарифу, учёт использования и денежные расчёты. Права по тарифу определяют, чем клиент может пользоваться сейчас; платёжный учёт фиксирует коммерческие события и движение денег. Опишите даты вступления в силу, пробные периоды, повышение и понижение тарифа, отмену, льготные периоды, уведомления поставщика о событиях, счета, зачёты, возвраты и сверку. Повторное или запоздалое событие от поставщика не должно дважды предоставить право, преждевременно его отнять или оставить доступ в постоянно противоречивом состоянии.
Настройкам клиента нужны схема, проверка, типовые значения, версия, сведения о происхождении, аудит, управляемое развёртывание и безопасное кеширование. Переключатели функций — это временное средство управления доступностью новой возможности, а не замена тарифным правам или постоянным ответвлениям продукта. Секреты хранятся в специализированной системе; приложение лишь ссылается на них, доступ ограничивается, а ключи регулярно меняются. Изменения настроек, влияющие на данные, расчёты или безопасность, требуют предварительного просмотра, согласования и плана восстановления так же, как изменения кода.
Масштабируйте работу с клиентами, а не только обработку запросов
Измеряйте не только итоги, но и распределение: число активных клиентских организаций, записи и объём хранилища на клиента, частоту запросов и фоновых задач, задержку, ошибки, время нахождения сообщений в очереди, число соединений, использование кеша, обращения к внешним поставщикам, нагрузку на поддержку и себестоимость единицы работы. Защищайте критические общие ресурсы квотами, управлением приёмом новой работы, ограничением параллельности для каждого клиента, классами нагрузки, предельным временем выполнения и режимами с пониженной функциональностью. Переносите клиентов с исключительно высокой нагрузкой в другую ячейку, когда данные действительно оправдывают новую эксплуатационную границу.
Изменения схемы и приложения превращаются в операции над всей совокупностью экземпляров. Пока разные версии сосуществуют, выпуски должны сохранять совместимость в обоих направлениях. Последовательно расширяйте схему, переносите данные и лишь затем сужайте её; отслеживайте версию каждой базы или ячейки; начинайте с небольших групп; приостанавливайте развёртывание при заранее определённых отклонениях в состоянии или сверке данных; после необратимого изменения данных исправляйте систему следующим выпуском, если возврат к предыдущей версии приложения уже опасен. Избегайте отдельных ветвей кода для конкретных клиентов, которые делают общее обновление невозможным.
Наблюдаемость должна показывать как всю платформу, так и отдельную клиентскую организацию. Идентификаторы клиентов с большим числом возможных значений не стоит добавлять к каждой метрике, поэтому сочетайте метрики с ограниченной размерностью с поиском в журналах, трассировкой, примерами, привязанными к метрикам и целевыми данными по отдельным клиентам. Определите целевые показатели для критических сценариев, ответственных за оповещения, разделение инцидентов по группам клиентов, доказательства резервного копирования и восстановления, прогнозы мощности и распределение затрат. Масштабирование удалось, если с ростом числа клиентов работа системы остаётся понятной и управляемой.
Когда помогут внешние архитекторы и дополнительные инженерные ресурсы
Внешний обзор полезен, когда изменение модели многоклиентности одновременно касается кода приложения, баз данных, идентификации, очередей, облачных границ, расчётов и поддержки; когда договор с крупным заказчиком добавляет требования к изоляции или месту хранения данных; когда риск доступа между клиентами неясен; либо когда общую модель нужно развить без простоя. Обзор должен опираться на реальные сценарии, схему и запросы, пути проверки прав, фоновые задачи, инфраструктуру, телеметрию, предыдущие инциденты и эксплуатационные ограничения.
Практический результат — пакет архитектурных решений: карта угроз и клиентских границ, подходящие модели и отклонённые альтернативы, последовательность миграции и сосуществования, сверка данных, проверки ключевых случаев, условия приёмки и остановки, наблюдаемость, план возврата к предыдущему состоянию или исправления следующим выпуском, ответственность, стоимость и передача знаний. После этого внедрение можно выполнять небольшими законченными частями. Внешняя экспертиза создаёт ценность, если внутренние инженеры способны самостоятельно эксплуатировать и развивать результат без постоянной зависимости.
- Выберите границу доверия: определите клиентскую организацию, модель членства, регуляторную и договорную изоляцию, индивидуальные настройки, устойчивость к чрезмерной нагрузке одного клиента, восстановление, удаление, регион хранения, распределение крупнейших клиентов и ответственного за эксплуатацию.
- Спроектируйте принадлежность данных: выберите общее, распределённое, изолированное или смешанное хранение; согласуйте с этой границей ключи, ограничения, индексы, хранилища, резервные копии, миграции и сверку.
- Создайте доверенный контекст: получайте идентификатор организации из проверенного домена, токена, учётных данных или членства; создавайте неизменный контекст и повторно проверяйте его в очередях, задачах, соединениях и интеграциях.
- Проверяйте право на ресурс и действие: на сервере проверяйте членство, роль или признаки, доступные возможности, принадлежность ресурса, его текущее состояние и привилегии поддержки; при отсутствии явного разрешения отказывайте в доступе и фиксируйте использование повышенных привилегий.
- Проверяйте изоляцию на всём пути выполнения: охватите одинаковые идентификаторы, запросы, связи, кеши, файлы, поиск, аналитику, журналы, фоновые задачи, экспорт, администрирование, одновременное выполнение, восстановление, удаление и локализацию инцидента.
- Управляйте всей совокупностью экземпляров: контролируйте фоновые задачи, права по тарифу, платёжные расчёты, настройки, квоты, версии схем, поэтапные выпуски, телеметрию, целевые показатели сервиса, восстановление, мощность, стоимость и перемещение между уровнями изоляции.
Какую модель многоклиентности выбрать для NestJS?
Безопаснее ли выделять отдельную базу данных каждому клиенту?
Стоит ли в NestJS создавать компоненты заново для каждого запроса ради клиентского контекста?
Как предотвратить доступ к данным другого клиента?
Как перенести клиента SaaS-платформы на изолированный уровень?
Опишите модель клиентских организаций и членства, критические сценарии, хранилища данных, идентификацию и проверку прав, фоновые задачи, расчёты, интеграции, требования к изоляции, распределение клиентов, целевые показатели сервиса, предыдущие инциденты и ожидаемый рост. GARNO.TECH сравнит подходящие модели и спроектирует готовую к миграции архитектуру с проверками, условиями приёмки, определённой ответственностью, эксплуатационными процедурами и планом восстановления предыдущего состояния.
Наши исследования
Исследование и разработка решений на основе искусственного интеллекта для оптимизации бизнес-процессов и повышения эффективности принятия решений.
Анализ моделей машинного обучения для прогнозной аналитики в сфере финансов, электронной коммерции и SaaS-платформ.
Исследование технологий обработки естественного языка и компьютерного зрения для усиления автоматизации, персонализации и поддержки клиентов.


