Тема
QueryRuntime
QueryRuntime — инфраструктурный корень Query Layer. Он не выполняет конкретный endpoint сам. Runtime создаёт общий QueryClient, общий executor запросов, управляет browser lifecycle и выдаёт ограниченные participant scopes, через которые создаются MobX stores.
Создание: createQueryRuntime(...).
Когда runtime не нужен
Для одной формы или одного изолированного экрана можно создать RequestStore, FetchStore или другой store напрямую. Это проще.
Когда runtime нужен
- несколько feature должны использовать общий query cache;
- нужны общие настройки retry, stale time и refetch on focus;
- нужно централизованно освободить все запросы;
- подключаются persistence, cross-tab sync или offline-инфраструктура;
- host и remote в Module Federation разделяют server state;
- нужны participant permissions и диагностика ресурсов.
Полный lifecycle приложения
ts
import { createQueryRuntime, type IScopedQueryRuntime } from "@dubium/query-layer/runtime"
// Runtime создаётся один раз на экземпляр приложения.
export const runtime = createQueryRuntime({
// Режим описывает назначение экземпляра.
mode: "application",
// applicationId отделяет cache этого приложения от других runtime.
identity: {
applicationId: "customer-portal",
version: "1.0.0",
},
// Не более пяти одновременных HTTP-попыток всех Store этого runtime.
maxConcurrentRequests: 5,
// Общая политика для всех query, созданных через scope.
queryClientConfig: {
defaultOptions: {
queries: {
staleTime: 30_000,
retry: 1,
},
mutations: {
retry: false,
},
},
refetchOnReconnect: true,
refetchOnWindowFocus: true,
},
})
// initialize подключает focus/online lifecycle QueryClient.
// Вызывайте его один раз до render React-root.
await runtime.initialize()
// Scope представляет конкретный mount приложения.
// Для participantType application явные namespace permissions не обязательны.
export const appScope: IScopedQueryRuntime = runtime.createParticipantScope({
participantId: "customer-portal-root",
participantType: "application",
version: "1.0.0",
})В обычном приложении можно создать один appScope. В Module Federation host создаёт отдельный scope для каждого mount remote. Это не глобальная переменная: host явно передаёт scope в функцию remote.mount(...).
Domain store через scope
ts
import { makeAutoObservable } from "mobx"
import type { INormalizedAxiosError } from "@dubium/query-layer"
import type { IScopedQueryRuntime } from "@dubium/query-layer/runtime"
import { getMyProfileApi, type Profile } from "../api/profile.api"
export class ProfileStore {
// Handler будет создан на общем client/executor runtime.
private readonly requestHandler
constructor(scope: IScopedQueryRuntime) {
this.requestHandler = scope.request.createStore({
api: getMyProfileApi,
options: {
retry: 1,
},
})
makeAutoObservable<this, "requestHandler">(
this,
{
requestHandler: false,
},
{
autoBind: true,
},
)
}
get profile(): Profile | null {
return this.requestHandler.data ?? null
}
get loading(): boolean {
return this.requestHandler.loading
}
get error(): INormalizedAxiosError | null {
return this.requestHandler.error
}
async fetch(): Promise<Profile | null> {
// API-функция не принимает values, поэтому execute вызывается без аргументов.
const response = await this.requestHandler.execute()
return response?.data ?? null
}
dispose(): void {
this.requestHandler.dispose()
}
}Scope имеет четыре factory-фасада:
ts
scope.request.createStore(...) // RequestStore
scope.fetch.createStore(...) // FetchStore
scope.query.createStore(...) // QueryStore
scope.query.createInfiniteStore(...) // InfiniteQueryStore
scope.mutation.createStore(...) // MutationStoreЭти stores автоматически используют общий client, получают owner context и освобождаются при scope.dispose().
Завершение приложения
ts
// Сначала прекращаем работу конкретного participant.
appScope.dispose()
// Затем отключаем runtime и его browser listeners.
await runtime.dispose()dispose() идемпотентен: повторный вызов безопасен. После dispose нельзя создавать новые scopes или повторно инициализировать этот экземпляр.
Конфигурация QueryRuntime
| Поле | Type | Default | Что делает |
|---|---|---|---|
identity | { applicationId: string; version?: string } | — | Обязательная идентичность runtime. applicationId не может быть пустым. |
maxConcurrentRequests | number | без ограничения | Лимит одновременных HTTP-попыток общего пула runtime. |
requestConcurrency | { default?: number; groups?: Record<string, number> } | без ограничения | Общий лимит и независимые лимиты именованных requestGroup. |
mode | "application" | "host" | "standalone-remote" | application по смыслу | Описывает сценарий использования. Доступ к данным фактически ограничивается participant registration. |
capabilities | TQueryRuntimeCapability[] | fetch, query, mutation, request и session, если настроена сессия | Возможности, объявляемые при MF compatibility handshake. |
protocolVersion | number | текущая версия протокола | Версия контракта host/remote. Должна быть положительным целым числом. |
queryClientConfig | IQueryClientConfig | defaults QueryClient | Общие query/mutation options, GC, focus и reconnect policy. |
requestExecutorOptions | IRequestExecutorOptions | defaults executor | Lifecycle events, clock и timeout manager общего executor. |
sessionCoordinator | ISessionCoordinatorContract | — | Готовый внешний coordinator авторизации. |
sessionCoordinatorOptions | ISessionCoordinatorOptions | — | Настройки coordinator, которым будет владеть runtime. Нельзя передавать вместе с sessionCoordinator. |
idGenerator | IIdGenerator | RuntimeIdGenerator | Генератор instance, operation и correlation ids. Полезен в тестах. |
retryRandom | () => number | Math.random | Управляемый random для детерминированных тестов встроенного retry jitter. |
Подробная семантика очереди, групп и повторных попыток описана в разделе «Конкурентность запросов и retry jitter».
queryClientConfig
| Поле | Type | Что делает |
|---|---|---|
defaultOptions.queries | Omit<Partial<IQueryOptions>, "queryFn" | "queryKey"> | Общие staleTime, gcTime, retry, networkMode и другие query defaults. |
defaultOptions.mutations | { retry?, retryDelay?, networkMode? } | Общая mutation policy. |
refetchOnReconnect | boolean | Обновлять активные stale query после восстановления сети. |
refetchOnWindowFocus | boolean | Обновлять активные stale query при возвращении фокуса. |
logger | IQueryLayerLogger | Получатель внутренних диагностических событий. |
clock | IClock | Внедряемый источник времени. Обычно нужен тестам. |
timeoutManager | ITimeoutManager | Внедряемые таймеры. Обычно нужны тестам. |
Сборка неактивного cache выполняется per-query по gcTime; отдельного глобального gcInterval в публичной конфигурации нет.
Runtime capabilities
Это не HTTP-методы и не права пользователя. Это список частей runtime API, которые host обещает remote:
| Значение | Что host предоставляет |
|---|---|
request | Создание RequestStore через scope.request. |
fetch | Создание FetchStore через scope.fetch. |
query | QueryStore, InfiniteQueryStore, чтение cache и invalidation через scope.query. |
mutation | Создание MutationStore через scope.mutation. |
session | Общий SessionCoordinator настроен в host runtime. |
Remote перечисляет только обязательные значения в requiredRuntimeCapabilities. Если host их не объявил, registration завершится ошибкой до создания scope.
Методы QueryRuntime
| Метод/поле | Type | Что делает |
|---|---|---|
initialize() | Promise<void> | Подключает QueryClient к browser focus/online lifecycle и инициализирует owned session coordinator. |
createParticipantScope(registration) | IScopedQueryRuntime | Регистрирует application/host/remote mount и возвращает ограниченные store factories. |
getProtocolDescriptor() | IQueryRuntimeProtocolDescriptor | Возвращает application id, version, protocol и capabilities для handshake. |
getDiagnostics() | IQueryRuntimeDiagnostics | Возвращает только безопасные счётчики без payload и credentials. |
dispose() | Promise<void> | Освобождает scopes, requests, session и QueryClient. |
activeParticipantCount | number | Текущее число живых scopes. |
identity | IRuntimeIdentity | Идентичность приложения. |
protocolVersion | number | Версия протокола scope. |
sessionCoordinator | ISessionCoordinatorContract | undefined | Общий coordinator, если был настроен. |
Participant registration
| Поле | Type | Что означает |
|---|---|---|
participantId | string | Стабильное имя модуля, например billing-remote. |
participantType | application | host | remote | Роль конкретного mount. |
version | string | Версия participant. |
instanceId | string | Уникальный id mount. Обычно генерируется runtime. |
protocolVersion | number | Требуемая версия протокола. |
minimumRuntimeVersion | string | Минимальная совместимая версия host runtime. |
requiredRuntimeCapabilities | TQueryRuntimeCapability[] | Возможности, без которых participant не работает. |
tenantId, userId | string | Раздел данных текущей сессии. |
capabilities | IParticipantCapabilities | Разрешённые query/mutation namespaces и global invalidation. Для remote обязательны. |
Для обычного приложения достаточно одного простого application participant. Все детали remote permissions, передачи scope и разделения кэша находятся в разделе Module Federation.
localStorage и runtime
Runtime сам ничего не записывает в localStorage. Сохранение включается отдельным persistence plugin и только для query с meta.persist === true. Подробнее: Persistence и localStorage в Module Federation.