Skip to content

Архитектура

Query Layer использует одно направление зависимостей:

mermaid
flowchart LR
    UI["React UI"] --> Domain["Domain MobX store"]
    Domain --> Handler["Query Layer store"]
    Handler --> Api["API-функция"]
    Api --> Axios["Axios client"]
    Axios --> Server["Backend"]

Ответ идёт обратно по той же цепочке. UI не перепрыгивает через domain store.

Transport

Transport знает общие сетевые настройки:

  • baseURL;
  • timeout;
  • cookies;
  • Authorization header;
  • refresh token flow.

Обычно приложение создаёт publicApi и authApi в одном файле.

Transport не знает, что такое форма регистрации, список проектов или React.

API-функция

Одна API-функция соответствует одному backend endpoint:

ts
export const getUserByIdApi = (
  userId: string,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<User>> => {
  return authApi.get<User>(`/api/v1/users/${userId}`, config)
}

Она отвечает за URL, HTTP-метод, DTO и runtime-проверку ответа. Последний AxiosRequestConfig нужен для AbortSignal.

Query Layer store

Store библиотеки отвечает за техническое состояние HTTP-операции:

  • запуск и отмена;
  • loading, status, error, data;
  • retry;
  • cache и dedup, если выбран query API;
  • lifecycle callbacks mutation;
  • pagination.

Это может быть RequestStore, FetchStore, QueryStore, MutationStore или InfiniteQueryStore.

Domain store

Domain store скрывает универсальный handler:

ts
export class UserDetailsStore {
  private readonly requestHandler = new FetchStore(...)

  get user(): User | null {
    return this.requestHandler.data ?? null
  }

  async fetch(userId: string): Promise<User | null> {
    const response = await this.requestHandler.fetch(userId)
    return response?.data ?? null
  }
}

Он отвечает за предметные имена, mapper формы, computed-поля и преобразование ошибки в domain error.

Если feature совсем простая и не имеет предметной логики, можно экспортировать настроенный FetchStore напрямую из data-layer. UI всё равно не должен импортировать API-функцию.

React UI

Компонент:

  • хранит локальные значения полей;
  • вызывает domain-методы;
  • читает observable-состояние;
  • отображает loading, data и error;
  • освобождает локальный store при unmount.

Компонент не выбирает URL, не создаёт Axios client и не разбирает DTO backend.

QueryRuntime

QueryRuntime находится над stores и управляет общими ресурсами:

mermaid
flowchart TD
    Runtime["QueryRuntime"] --> ScopeA["Application scope"]
    Runtime --> ScopeB["Remote scope"]
    ScopeA --> StoresA["MobX stores"]
    ScopeB --> StoresB["MobX stores"]

Он нужен не каждой feature. Runtime создаётся на composition root, если нужен общий cache, plugins или Module Federation.

Правило зависимостей

Допустимо:

text
UI → domain store → query-layer store → API-функция → Axios client

Не допускайте обратных импортов:

  • API-функция не импортирует store;
  • store не импортирует React;
  • UI не импортирует API-функцию или Axios client;
  • remote не получает root runtime.

Практический полный пример: Flow разработчика.