Statelyai e XState: modelagem de estados complexos com máquinas de estado
1. Fundamentos da Modelagem de Estados com Máquinas de Estado
1.1. Conceitos clássicos: estados, transições, eventos e ações
Máquinas de estado finito (FSM) são modelos computacionais compostos por um conjunto finito de estados, transições entre eles, eventos que disparam essas transições e ações executadas durante o processo. Um sistema pode estar em apenas um estado por vez, e cada transição é determinística: dado um estado atual e um evento, a máquina sempre irá para o mesmo próximo estado.
Estado A --[evento X]--> Estado B
Estado B --[evento Y]--> Estado C
1.2. Por que máquinas de estado finito (FSM) são insuficientes para sistemas modernos
FSMs clássicas sofrem de "explosão de estados" em sistemas complexos. Considere um formulário de cadastro com validação de email, verificação de CPF, upload de documento e confirmação por SMS. Representar todas as combinações possíveis como estados planos resulta dezenas de estados — muitos deles redundantes. Além disso, FSMs não suportam concorrência real: se dois processos precisam rodar simultaneamente (ex: validação de dados enquanto o usuário digita), a modelagem torna-se artificial e frágil.
1.3. Introdução ao paradigma de statecharts: hierarquia, paralelismo e guardas
David Harel propôs os statecharts em 1987 como extensão das FSMs. Três conceitos fundamentais resolvem as limitações:
- Hierarquia: estados podem conter subestados (aninhamento)
- Paralelismo: regiões ortogonais executam simultaneamente
- Guardas: condições booleanas que controlam transições
Esses elementos permitem modelar sistemas reais com clareza e sem redundância.
2. XState: A Biblioteca que Traduz Statecharts para Código
2.1. Instalação e configuração básica do XState
npm install xstate @xstate/react
2.2. Sintaxe de definição de máquinas: estados, eventos e transições simples
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inativo',
states: {
inativo: {
on: { TOGGLE: 'ativo' }
},
ativo: {
on: { TOGGLE: 'inativo' }
}
}
});
2.3. Tipos de nós: estados atômicos, compostos, paralelos e finais
- Atômico: estado simples sem filhos
- Composto: contém subestados (hierarquia)
- Paralelo: múltiplas regiões simultâneas
- Final: estado terminal que encerra a máquina
const compoundMachine = createMachine({
initial: 'idle',
states: {
idle: { on: { START: 'working' } },
working: {
type: 'compound',
initial: 'processing',
states: {
processing: { on: { DONE: 'completed' } },
completed: { type: 'final' }
}
}
}
});
3. Modelagem de Estados Complexos com Hierarquia e Paralelismo
3.1. Estados aninhados: representando subfluxos de negócio
Um assistente de compras pode ter o estado checkout com subestados carrinho, pagamento e confirmacao. Cada subestado gerencia suas próprias transições internas.
const checkoutMachine = createMachine({
initial: 'carrinho',
states: {
carrinho: { on: { PROSSEGUIR: 'pagamento' } },
pagamento: {
initial: 'aguardando',
states: {
aguardando: { on: { PAGAR: 'processando' } },
processando: { on: { SUCESSO: 'pago', FALHA: 'falhou' } },
pago: { type: 'final' },
falhou: { on: { TENTAR_NOVAMENTE: 'aguardando' } }
}
},
confirmacao: { type: 'final' }
}
});
3.2. Regiões paralelas: executando múltiplos estados simultaneamente
Um dashboard de monitoramento pode exibir status de servidor e conexão de banco em paralelo.
const dashboardMachine = createMachine({
type: 'parallel',
states: {
servidor: {
initial: 'online',
states: {
online: { on: { FALHA: 'offline' } },
offline: { on: { RECUPERAR: 'online' } }
}
},
banco: {
initial: 'conectado',
states: {
conectado: { on: { PERDA: 'desconectado' } },
desconectado: { on: { RECONECTAR: 'conectado' } }
}
}
}
});
3.3. Estados finais e estados de histórico: retomando fluxos interrompidos
Estados de histórico (history) permitem retornar ao último subestado ativo antes de uma transição. Útil em formulários que o usuário pode salvar e retomar.
const formMachine = createMachine({
initial: 'edicao',
states: {
edicao: {
initial: 'dados_pessoais',
states: {
dados_pessoais: { on: { AVANCAR: 'endereco' } },
endereco: { on: { AVANCAR: 'revisao' } },
revisao: { on: { ENVIAR: '#final' } }
},
on: {
PAUSAR: 'pausado'
}
},
pausado: {
type: 'compound',
on: {
RETOMAR: 'edicao.history'
}
}
}
});
4. Ações, Guardas e Serviços: Comportamento Dinâmico
4.1. Ações de entrada, saída e transição: efeitos colaterais controlados
const machine = createMachine({
initial: 'aguardando',
states: {
aguardando: {
entry: 'logEntrada',
exit: 'logSaida',
on: {
INICIAR: {
target: 'processando',
actions: 'logTransicao'
}
}
},
processando: { /* ... */ }
}
}, {
actions: {
logEntrada: () => console.log('Entrou em aguardando'),
logSaida: () => console.log('Saiu de aguardando'),
logTransicao: () => console.log('Transição INICIAR')
}
});
4.2. Guardas condicionais: decidindo rotas com base em contexto
const authMachine = createMachine({
context: { usuario: null, permissoes: [] },
initial: 'verificando',
states: {
verificando: {
on: {
LOGIN: [
{ target: 'admin', cond: 'ehAdmin' },
{ target: 'usuario', cond: 'ehUsuario' },
{ target: 'negado' }
]
}
},
admin: { /* ... */ },
usuario: { /* ... */ },
negado: { /* ... */ }
}
}, {
guards: {
ehAdmin: (ctx) => ctx.permissoes.includes('admin'),
ehUsuario: (ctx) => ctx.usuario !== null
}
});
4.3. Invocação de serviços: chamadas assíncronas, promises e máquinas filhas
const fetchMachine = createMachine({
initial: 'idle',
states: {
idle: { on: { FETCHAR: 'carregando' } },
carregando: {
invoke: {
src: 'buscarDados',
onDone: { target: 'sucesso', actions: 'salvarDados' },
onError: { target: 'erro', actions: 'logErro' }
}
},
sucesso: { /* ... */ },
erro: { on: { TENTAR_NOVAMENTE: 'carregando' } }
}
}, {
services: {
buscarDados: () => fetch('/api/dados').then(r => r.json())
}
});
5. Statelyai: Ferramentas Visuais para Design e Depuração
5.1. Stately Studio: editor gráfico de statecharts
O Stately Studio (anteriormente XState Viz) é um ambiente visual onde você arrasta e solta estados, conecta transições, define guardas e ações — tudo com feedback visual imediato.
5.2. Visualização e simulação de máquinas em tempo real
Durante a simulação, você pode disparar eventos manualmente, observar mudanças de estado, inspecionar o contexto e visualizar o caminho percorrido. Ideal para depuração e documentação.
5.3. Exportação de código e integração com XState
Após modelar visualmente, o Studio exporta código pronto para uso com XState, mantendo toda a lógica de estados, guardas e ações. A integração é bidirecional: você pode importar máquinas existentes para edição visual.
6. Padrões Avançados e Casos de Uso Reais
6.1. Máquinas de estado para formulários multi-etapas
Formulários com validação em cada etapa, salvamento automático e retomada posterior são modelados naturalmente com estados aninhados e histórico.
6.2. Gerenciamento de fluxos de autenticação e autorização
Fluxos de login, recuperação de senha, autenticação de dois fatores e refresh de token encaixam-se perfeitamente em máquinas com regiões paralelas e guardas condicionais.
6.3. Orquestração de workflows com máquinas aninhadas e eventos globais
Sistemas de aprovação de documentos, pipelines de CI/CD e processos de onboarding podem ser modelados como máquinas pai que orquestram máquinas filhas, comunicando-se via eventos.
7. Integração com Frameworks e Ecossistema
7.1. Uso com React: hooks useMachine e useService
import { useMachine } from '@xstate/react';
import { toggleMachine } from './toggleMachine';
function Toggle() {
const [state, send] = useMachine(toggleMachine);
return (
<button onClick={() => send('TOGGLE')}>
{state.matches('ativo') ? 'Ligado' : 'Desligado'}
</button>
);
}
7.2. Integração com Vue, Angular e Svelte
XState oferece pacotes oficiais para Vue (@xstate/vue), Angular (@xstate/angular) e adaptadores para Svelte. A lógica permanece independente do framework.
7.3. Testabilidade: escrevendo testes unitários para máquinas de estado
import { interpret } from 'xstate';
import { toggleMachine } from './toggleMachine';
test('deve alternar entre ativo e inativo', () => {
const service = interpret(toggleMachine).start();
expect(service.state.matches('inativo')).toBe(true);
service.send('TOGGLE');
expect(service.state.matches('ativo')).toBe(true);
service.send('TOGGLE');
expect(service.state.matches('inativo')).toBe(true);
});
8. Limitações, Armadilhas e Boas Práticas
8.1. Quando evitar máquinas de estado: cenários de baixa complexidade
Para componentes com 2-3 estados simples (ex: botão de like), uma máquina de estado é excesso de engenharia. Use useState ou estado local.
8.2. Gerenciamento de estado global vs. máquinas locais
Máquinas de estado não substituem gerenciadores de estado global (Redux, Zustand). Use máquinas para lógica de fluxo e estado global para dados compartilhados entre componentes não relacionados.
8.3. Boas práticas: nomes de eventos, versionamento e documentação visual
- Use eventos no passado (
DADOS_CARREGADOS,FORMULARIO_ENVIADO) - Versionamento semântico para máquinas exportadas
- Mantenha diagramas atualizados no Stately Studio como documentação viva
Referências
- Documentação oficial do XState — Guia completo de referência, tutoriais e exemplos de uso da biblioteca
- Stately Studio - Editor Visual de Statecharts — Ferramenta gráfica para modelar, simular e exportar máquinas de estado
- Introdução a Statecharts por David Harel (artigo original) — Paper seminal que definiu o paradigma dos statecharts
- XState no GitHub — Repositório oficial com código fonte, issues e exemplos da comunidade
- Padrões com Statecharts em Aplicações React — Artigo prático sobre modelagem de formulários, autenticação e workflows com XState
- Testando Máquinas de Estado com XState — Guia oficial sobre estratégias de teste unitário e de integração para máquinas
- XState vs Redux: Quando Usar Cada Um — Comparação entre máquinas de estado e gerenciadores de estado tradicionais