--- title: Realtime description: "Работа с push-данными от сервера: подписки и события." keywords: [realtime, websocket, sse, подписка, swr subscription, useSWRSubscription, push, события] --- # Realtime Работа с push-данными от сервера: подписки и события. ## Принципы - **Клиент realtime — в `infrastructure/`** отдельным модулем по имени канала. То же правило, что и для REST: никаких прямых соединений в коде приложения. - **Подписка — единица потребления.** Клиент даёт функцию `subscribe(topic, handler) → unsubscribe`. Внутри — конкретный транспорт. - **Использование на клиенте — два сценария:** - **`useSWRSubscription`** — для данных, которые показываются в UI и должны кешироваться/синхронизироваться с REST. - **Прямая подписка** — для побочных эффектов (тосты, нотификации, аналитика), не привязанных к рендеру. ## Размещение клиента ```text src/infrastructure/ └── {channel-name}/ ├── connection.ts # установление соединения, реконнект ├── subscribe.ts # subscribe(topic, handler) → unsubscribe ├── types.ts └── index.ts ``` ## Использование через SWR ```tsx 'use client' import useSWRSubscription from 'swr/subscription' import { subscribe } from 'infrastructure/notifications' export function NotificationCounter() { const { data: count } = useSWRSubscription( ['notifications', 'count'], (key, { next }) => subscribe('notifications.count', (value: number) => next(null, value)), ) return {count ?? 0} } ``` Плюсы: кеш и дедупликация подписки между несколькими местами рендера; единая модель данных с REST. ## Прямая подписка Для побочных эффектов, которые не влияют на состояние UI напрямую: ```tsx 'use client' import { useEffect } from 'react' import { subscribe } from 'infrastructure/notifications' import { showToast } from 'ui/toast' export function NotificationsToaster() { useEffect(() => { return subscribe('notifications.new', (notification) => { showToast(notification.message) }) }, []) return null } ``` Возврат `unsubscribe` из `useEffect` обязателен — иначе утечка подписки. ## Запрет прямых соединений Создавать `new WebSocket(...)`, `new EventSource(...)` или подписываться на событийные шины напрямую в коде приложения — запрещено. Все соединения проходят через клиент в `infrastructure/`. Исключения — точечные и обоснованные (например, диагностический скрипт), помечаются комментарием.