← Назад к вопросам

Для чего нужна машинерия в реализации архитектуры?

2.2 Middle🔥 151 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

State Machine (Машина состояний) в архитектуре

State Machine (конечный автомат) — это архитектурный паттерн, который определяет допустимые переходы между состояниями и гарантирует, что приложение всегда находится в валидном состоянии.

Что такое State Machine

State Machine состоит из:

  1. Состояния (States) — возможные условия системы
  2. События (Events) — триггеры для переходов
  3. Переходы (Transitions) — правила перехода между состояниями
  4. Действия (Actions) — побочные эффекты при переходе
// Простая машина состояний для формы оформления покупки
const states = {
  'IDLE': { /* начальное состояние */ },
  'LOADING': { /* загружаем данные */ },
  'SUCCESS': { /* успех */ },
  'ERROR': { /* ошибка */ },
};

const transitions = {
  'IDLE': {
    'SUBMIT': 'LOADING', // На нажатие Submit идем в LOADING
  },
  'LOADING': {
    'SUCCESS': 'SUCCESS', // При успехе идем в SUCCESS
    'ERROR': 'ERROR',     // При ошибке идем в ERROR
  },
  'SUCCESS': {
    'RESET': 'IDLE', // Можно вернуться в начало
  },
  'ERROR': {
    'RETRY': 'LOADING', // Повторить попытку
    'RESET': 'IDLE',    // Или вернуться в начало
  },
};

Проблемы без State Machine

1. Несогласованные состояния (impossible states)

// БЕЗ State Machine — можно попасть в невозможное состояние
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

// Как быть если все true одновременно?
if (loading && error && data) {
  // Какой показать состояние?
}

// Возможны ошибки:
setError('Ошибка!');
setLoading(true); // Забыли выставить false
// Теперь loading=true И error='Ошибка!' одновременно

2. Забытые переходы

// Пользователь может нажать кнопку в неправильный момент
const handleSubmit = () => {
  if (loading) return; // Защита отломается если забыть
  
  setLoading(true);
  // ...
};

// А если условие забудут?
const handleRetry = () => {
  // Пользователь может нажать Retry когда LOADING
  // и запросится еще один запрос, причем параллельный
};

3. Сложная логика взаимодействий

// Множество условий и if-else
function render() {
  if (loading && !error) {
    return <Spinner />;
  } else if (!loading && error) {
    return <Error message={error} />;
  } else if (!loading && !error && data) {
    return <Success data={data} />;
  } else if (!loading && !error && !data) {
    return <Empty />;
  }
  // Какой случай забыли?
}

Решение: State Machine

Простая реализация

class OrderForm {
  constructor() {
    this.state = 'IDLE';
  }

  // Переводим в новое состояние только через validate
  setState(newState, event) {
    const transitions = {
      'IDLE': { 'SUBMIT': 'LOADING' },
      'LOADING': { 'SUCCESS': 'SUCCESS', 'ERROR': 'ERROR' },
      'SUCCESS': { 'RESET': 'IDLE' },
      'ERROR': { 'RETRY': 'LOADING', 'RESET': 'IDLE' },
    };

    const allowed = transitions[this.state]?.[event];
    if (!allowed) {
      throw new Error(`Cannot transition from ${this.state} on ${event}`);
    }

    this.state = allowed;
    this.executeAction(this.state, event);
    console.log(`Transitioned: ${this.state}`);
  }

  executeAction(state, event) {
    // Выполнить побочные эффекты
    const actions = {
      'LOADING': () => this.startRequest(),
      'SUCCESS': () => this.showSuccessMessage(),
      'ERROR': () => this.showErrorMessage(),
    };
    actions[state]?.();
  }

  startRequest() { /* Отправить запрос */ }
  showSuccessMessage() { /* Показать успех */ }
  showErrorMessage() { /* Показать ошибку */ }
}

// Использование
const form = new OrderForm();
form.setState(form.state, 'SUBMIT');
// Результат: state = 'LOADING'

form.setState(form.state, 'SUCCESS');
// Результат: state = 'SUCCESS'

form.setState(form.state, 'RETRY');
// Ошибка! Нельзя RETRY из SUCCESS

XState библиотека (стандарт индустрии)

import { createMachine, interpret } from 'xstate';

const checkoutMachine = createMachine({
  id: 'checkout',
  initial: 'idle',
  states: {
    idle: {
      on: {
        SUBMIT: 'loading',
      },
    },
    loading: {
      invoke: {
        src: 'submitOrder', // Побочный эффект
        onDone: {
          target: 'success',
          actions: 'setData',
        },
        onError: {
          target: 'error',
          actions: 'setError',
        },
      },
    },
    success: {
      on: {
        RESET: 'idle',
      },
    },
    error: {
      on: {
        RETRY: 'loading',
        RESET: 'idle',
      },
    },
  },
});

// Использование в React
import { useMachine } from '@xstate/react';

function CheckoutForm() {
  const [state, send] = useMachine(checkoutMachine);

  return (
    <div>
      {state.matches('idle') && (
        <form onSubmit={() => send('SUBMIT')}>
          <input type="text" placeholder="Email" />
          <button type="submit">Оформить</button>
        </form>
      )}
      
      {state.matches('loading') && <div>Загрузка...</div>}
      
      {state.matches('success') && (
        <div>
          Спасибо за покупку!
          <button onClick={() => send('RESET')}>Новый заказ</button>
        </div>
      )}
      
      {state.matches('error') && (
        <div>
          Ошибка: {state.context.error}
          <button onClick={() => send('RETRY')}>Повторить</button>
          <button onClick={() => send('RESET')}>Вернуться</button>
        </div>
      )}
    </div>
  );
}

Практические примеры

1. Машина состояний для аутентификации

const authMachine = createMachine({
  id: 'auth',
  initial: 'checking',
  states: {
    checking: {
      invoke: {
        src: 'checkToken',
        onDone: [
          { target: 'authenticated', cond: 'isValid' },
          { target: 'unauthenticated' },
        ],
      },
    },
    unauthenticated: {
      on: {
        LOGIN: 'authenticating',
      },
    },
    authenticating: {
      invoke: {
        src: 'login',
        onDone: { target: 'authenticated', actions: 'saveToken' },
        onError: { target: 'unauthenticated' },
      },
    },
    authenticated: {
      on: {
        LOGOUT: { target: 'unauthenticated', actions: 'clearToken' },
      },
    },
  },
});

2. Машина состояний для модального окна

const modalMachine = createMachine({
  id: 'modal',
  initial: 'closed',
  states: {
    closed: {
      on: { OPEN: 'open' },
    },
    open: {
      on: {
        CLOSE: 'closed',
        SUBMIT: 'submitting',
      },
    },
    submitting: {
      invoke: {
        src: 'submit',
        onDone: { target: 'closed', actions: 'showSuccess' },
        onError: { target: 'open', actions: 'showError' },
      },
    },
  },
});

3. Машина состояний для видеоплеера

const playerMachine = createMachine({
  id: 'player',
  initial: 'idle',
  states: {
    idle: {
      on: { PLAY: 'playing', LOAD: 'loading' },
    },
    loading: {
      invoke: {
        src: 'fetchVideo',
        onDone: { target: 'playing' },
        onError: { target: 'idle' },
      },
    },
    playing: {
      on: {
        PAUSE: 'paused',
        STOP: 'idle',
        SEEK: 'seeking',
      },
    },
    paused: {
      on: {
        PLAY: 'playing',
        STOP: 'idle',
      },
    },
    seeking: {
      invoke: {
        src: 'seek',
        onDone: { target: 'playing' },
      },
    },
  },
});

Преимущества State Machine

1. Невозможны невалидные состояния

// Компилятор или runtime проверит переходы
state.send('INVALID_EVENT'); // Ошибка!

2. Явная документация

// Граф состояний визуально показывает логику
// Легко читать и понимать
idle -> loading -> success / error

3. Тестируемость

test('переход из idle в loading на SUBMIT', () => {
  const state = machine.transition('idle', 'SUBMIT');
  expect(state.value).toBe('loading');
});

4. Отладка

// Легко отследить состояния и переходы
// Можно визуализировать граф XState

Когда использовать

  • Сложные формы с несколькими состояниями
  • Аутентификация (checking -> authenticated -> unauthenticated)
  • Модальные окна (closed -> open -> submitting)
  • Медиаплеер (playing -> paused -> stopped)
  • Мастер (wizard) (step1 -> step2 -> step3 -> success)
  • Автоматизированные системы (заказы, платежи)

Выводы

  • State Machine предотвращает невалидные состояния
  • Делает код более предсказуемым и безопасным
  • XState — лучший выбор для сложных приложений
  • Документирует логику приложения через граф состояний
  • Улучшает тестируемость и отладку
  • Масштабируется при добавлении новых функций
Для чего нужна машинерия в реализации архитектуры? | PrepBro