O que é uma máquina de estados e sua importância
Sistemas como reservas de viagem passam por transições complexas: "pendente → pago → confirmado → concluído". Implementar essas mudanças com estruturas condicionais tradicionais gera complexidade. Máquinas de estados solucionam isso através de:
- Estados: Situações possíveis do sistema
- Eventos: Ações que disparam transições
- Transições: Regras de mudança entre estados
Conceitos fundamentais
Componentes principais
public enum StatusReserva {
AGUARDANDO_PAGAMENTO,
PAGO,
CONFIRMADO,
CONCLUIDO,
CANCELADO
}
public enum EventoReserva {
EFETUAR_PAGAMENTO,
CONFIRMAR_RESERVA,
FINALIZAR
}
@Configuration
@EnableStateMachine
public class ConfiguracaoMaquina extends StateMachineConfigurerAdapter<StatusReserva, EventoReserva> {
@Override
public void configure(StateMachineStateConfigurer<StatusReserva, EventoReserva> estados) throws Exception {
estados.withStates()
.initial(StatusReserva.AGUARDANDO_PAGAMENTO)
.states(EnumSet.allOf(StatusReserva.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<StatusReserva, EventoReserva> transicoes) throws Exception {
transicoes
.withExternal()
.source(StatusReserva.AGUARDANDO_PAGAMENTO)
.target(StatusReserva.PAGO)
.event(EventoReserva.EFETUAR_PAGAMENTO)
.and()
.withExternal()
.source(StatusReserva.PAGO)
.target(StatusReserva.CONFIRMADO)
.event(EventoReserva.CONFIRMAR_RESERVA)
.and()
.withExternal()
.source(StatusReserva.CONFIRMADO)
.target(StatusReserva.CONCLUIDO)
.event(EventoReserva.FINALIZAR);
}
}
Persistência de estado
@Service
public class ServicoReserva {
private final StateMachineFactory<StatusReserva, EventoReserva> fabrica;
private final StateMachinePersist<StatusReserva, EventoReserva, String> persistencia;
public boolean processarPagamento(String idReserva) {
StateMachine<StatusReserva, EventoReserva> maquina = restaurarMaquina(idReserva);
boolean sucesso = maquina.sendEvent(EventoReserva.EFETUAR_PAGAMENTO);
if (sucesso) {
salvarMaquina(idReserva, maquina);
}
return sucesso;
}
private StateMachine<StatusReserva, EventoReserva> restaurarMaquina(String id) {
return persistencia.restore(fabrica.getStateMachine(), id);
}
private void salvarMaquina(String id, StateMachine<StatusReserva, EventoReserva> maquina) {
persistencia.persist(maquina, id);
}
}
Sistema de aprovação com estados paralelos
@Configuration
@EnableStateMachine
public class ConfiguracaoFluxo extends StateMachineConfigurerAdapter<EstadoAprovacao, EventoAprovacao> {
@Override
public void configure(StateMachineStateConfigurer<EstadoAprovacao, EventoAprovacao> estados) throws Exception {
estados
.withStates()
.initial(EstadoAprovacao.RASCUNHO)
.fork(EstadoAprovacao.DIVERGENCIA)
.join(EstadoAprovacao.CONVERGENCIA)
.state(EstadoAprovacao.FINALIZADO)
.and()
.withStates()
.parent(EstadoAprovacao.DIVERGENCIA)
.initial(EstadoAprovacao.APROVACAO_TECNICA)
.state(EstadoAprovacao.TECNICA_APROVADA)
.and()
.withStates()
.parent(EstadoAprovacao.DIVERGENCIA)
.initial(EstadoAprovacao.APROVACAO_FINANCEIRA)
.state(EstadoAprovacao.FINANCEIRA_APROVADA);
}
}
Ouvitnes e verificações
@WithStateMachine
public class OuvinteReserva {
@OnTransition(target = "PAGO")
public void aoPagar(Message<EventoReserva> mensagem) {
String id = mensagem.getHeaders().get("idReserva", String.class);
// Lógica pós-pagamento
}
}
@Component
public class ValidadorReserva {
@Bean
public Guard<StatusReserva, EventoReserva> validadorPagamento() {
return contexto -> {
String id = contexto.getMessageHeader("idReserva");
return validarDisponibilidade(id);
};
}
}
Visualização e consistência
@GetMapping("/diagrama/{id}")
public ResponseEntity<String> gerarDiagrama(@PathVariable String id) {
StatusReserva estadoAtual = servico.obterEstado(id);
String diagrama = "// Código PlantUML com estado atual destacado";
return ResponseEntity.ok(diagrama);
}
// Abordagem com bloqueio distribuído
public boolean transicionar(String id, EventoReserva evento) {
RLock lock = redisson.getLock("reserva:" + id);
lock.lock();
try {
StateMachine<StatusReserva, EventoReserva> maquina = restaurarMaquina(id);
boolean resultado = maquina.sendEvent(evento);
salvarMaquina(id, maquina);
return resultado;
} finally {
lock.unlock();
}
}
Otimizações recomendadas
- Cache de máquinas de estado para acesso frequente
- Processamento assíncrono de eventos
- Persistência em lote de transições
- Separação de leitura/escrita em bancos distintos