Máquina de Estados Spring: Fundamentos e Aplicações Práticas

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

Tags: Spring State Machine java Transição de Estados Persistência de Estado Spring Framework

Publicado em 7-4 08:33