System Design Space
Граф знанийНастройки

Обновлено: 21 апреля 2026 г. в 16:55

Состояние и кэш во frontend-приложении

средний

Как разделять local/UI/server/URL/form state, строить invalidation и optimistic updates и не превращать клиент в неуправляемый граф зависимостей.

Почти каждое сложное frontend-приложение ломается не об один большой state store, а о путаницу между local state, server state, URL state, form state и cache invalidation. Чем раньше эти контуры становятся явными, тем меньше хаоса возникает при обновлении UI и расследовании багов.

Эта глава полезна тем, что связывает state management с реальными сценариями: optimistic updates, background refetch, offline queue, realtime updates и возврат пользователя на предыдущий экран. Она помогает обсуждать состояние как набор разных классов данных с разной жизнью, а не как одну универсальную корзину.

Для review и интервью материал хорош тем, что дает способ честно говорить о главной боли frontend: где проходит источник правды, кто владеет кэшем и почему invalidation обычно оказывается труднее, чем initial fetch.

Практическая польза главы

Практика проектирования

Переводите знания о границах client state, cache invalidation и предсказуемом обновлении UI в конкретные решения по composition, владению (ownership) и runtime-поведению клиентской системы.

Качество решений

Оценивайте архитектуру по измеримым эффектам: скорость delivery, стабильность UI, наблюдаемость, цена изменений и эксплуатационные риски.

Аргументация на интервью

Стройте ответ как цепочку problem -> constraints -> architecture -> компромиссы (trade-offs) -> migration path, с явной аргументацией frontend-выбора.

Формулировка компромиссов

Фиксируйте компромиссы вокруг границах client state, cache invalidation и предсказуемом обновлении UI: масштаб команды, технический долг, performance budget и долгосрочная поддержка.

Контекст

Стратегии кэширования

Во frontend invalidation столь же сложен, как и в backend, только пользователь видит каждую ошибку прямо на экране.

Читать обзор

Почти в каждом сложном frontend спор о state management на самом деле является спором о том, какие классы состояния мы смешали в одну корзину. Local UI state, server state, URL state и form state живут по-разному и ломаются по-разному.

Поэтому ключевой вопрос звучит так: где находится source of truth и кто отвечает за invalidation после пользовательского действия, background refresh или realtime update.

Классы состояния

Local UI state

Modal visibility, hover state, unsaved draft fragments и temporary interaction state. Обычно живет ближе всего к компоненту и не требует глобальной синхронизации.

Server state

Данные, источник правды для которых находится на backend: profile, feed items, dashboard widgets, orders. Их жизнь определяется cache policy, freshness и invalidation.

URL state

Filters, pagination cursor, active tabs и search params. Если пользователь ожидает deep link или recoverability after refresh, state, скорее всего, должен быть сериализуемым в URL.

Form state

Validation, dirty flags, autosave, field-level errors и optimistic submission. Форма почти всегда требует отдельной модели жизни по сравнению с обычным query cache.

Связано

Design Google Docs collaborative editor

Realtime collaboration быстро показывает, насколько дорого обходятся неявные правила merge и rollback.

Открыть главу

Паттерны обновления и invalidation

Optimistic update с явным rollback path

Подходит, когда пользовательский intent дорогой и понятен: like, rename, toggle. Главное - заранее определить, как UI объясняет конфликт или откат.

Background revalidation

Удобна для screens with stale-tolerant data: сначала показываем кэш, затем тихо сверяемся с сервером и обновляем только изменившиеся зоны.

Targeted invalidation по доменным ключам

Вместо 'очистить все' лучше инвалидировать ровно те query groups, которые зависят от измененного ресурса. Это снижает network noise и прыжки интерфейса.

Realtime merge policy

WebSocket/push updates должны иметь правила слияния с локальным optimistic state. Иначе UI начинает мигать между локальной и удаленной версиями.

Частые антипаттерны

  • Один giant global store для всего приложения без различия между server и UI state.
  • Скрытые side effects в selectors и derived state, которые трудно объяснить и протестировать.
  • Сброс query cache при каждом route change, из-за чего приложение постоянно теряет контекст пользователя.
  • Отсутствие явной стратегии для stale data, optimistic rollback и reconnect после сетевого сбоя.

Практическое правило

Чем ближе состояние к пользовательскому действию и чем короче его жизнь, тем ближе оно должно находиться к модулю или экрану. Чем ближе состояние к backend truth и повторному использованию на нескольких экранах, тем больше ему нужен typed cache layer с явной invalidation policy.

Связанные главы

Чтобы отмечать прохождение, включи трекинг в Настройки