← Назад к вопросам
Для чего нужна машинерия в реализации архитектуры?
2.2 Middle🔥 151 комментариев
#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
State Machine (Машина состояний) в архитектуре
State Machine (конечный автомат) — это архитектурный паттерн, который определяет допустимые переходы между состояниями и гарантирует, что приложение всегда находится в валидном состоянии.
Что такое State Machine
State Machine состоит из:
- Состояния (States) — возможные условия системы
- События (Events) — триггеры для переходов
- Переходы (Transitions) — правила перехода между состояниями
- Действия (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 — лучший выбор для сложных приложений
- Документирует логику приложения через граф состояний
- Улучшает тестируемость и отладку
- Масштабируется при добавлении новых функций