Что такое изменение состояния системы?

«Что такое изменение состояния системы?» — вопрос из категории Архитектура, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Изменение состояния системы — это модификация любых данных, которые влияют на последующее поведение приложения. В Node.js-приложении состояние может быть:

  • В памяти процесса: Глобальные переменные, кэш, данные в замыканиях.
  • Во внешнем хранилище: Базы данных (PostgreSQL, MongoDB), кэши (Redis), файловая система.
  • Состояние подключений: Сессии пользователей, WebSocket-соединения.

Примеры и проблемы:

  1. Разделяемое состояние в памяти (проблема):

    // Глобальное состояние - АНТИПАТТЕРН для многопользовательского приложения
    let userSessions = {}; // Состояние в памяти
    
    app.post('/login', (req, res) => {
      userSessions[req.body.userId] = { lastActive: Date.now() }; // Изменение состояния
    });
    
    app.get('/profile', (req, res) => {
      const session = userSessions[req.query.userId]; // Чтение состояния
    });

    Проблемы: Это состояние теряется при перезагрузке сервера, его сложно разделять между несколькими инстансами приложения (при горизонтальном масштабировании), и возможны гонки данных (race conditions) при параллельных запросах.

  2. Управляемое состояние (решение): Вынос состояния во внешний, общий для всех инстансов, источник.

    // Использование Redis для хранения сессий
    const redisClient = require('redis').createClient();
    
    app.post('/login', async (req, res) => {
      const sessionKey = `session:${req.body.userId}`;
      // Состояние изменяется во внешнем хранилище
      await redisClient.set(sessionKey, JSON.stringify({ lastActive: Date.now() }), 'EX', 3600);
      res.send('OK');
    });

Ключевые принципы работы с состоянием в Node.js:

  • Избегайте разделяемого изменяемого состояния в памяти между запросами. Используйте внешние хранилища (БД, Redis).
  • Стремитесь к иммутабельности. Создавайте новые объекты вместо изменения существующих. Это упрощает рассуждение о коде и предотвращает случайные мутации.

    // Вместо мутации
    const updateUserBad = (user, newEmail) => { user.email = newEmail; };
    
    // Предпочтительно: иммутабельное обновление
    const updateUserGood = (user, newEmail) => ({
      ...user, // Spread оператор создает новый объект
      email: newEmail,
      updatedAt: Date.now()
    });
  • Инкапсулируйте состояние. Используйте классы или модули для управления конкретными частями состояния.
  • Для сложных клиентских состояний (например, в SPA) можно использовать паттерны вроде Flux/Redux, которые также применимы и на сервере в Node.js для управления состоянием конкретного бизнес-процесса.

Правильное управление состоянием — основа для создания предсказуемых, масштабируемых и отказоустойчивых приложений на Node.js.