3

I have a simple order processing application and trying to apply spring state machine for handling states of the order. And I wondering how can I handle order's states for the same order during multiple request from rest service.

Order states:

enum OrderEvents {
    FULFILL,
    PAY,
    CANCEL
}

Order events:

enum OrderStates {
    SUBMITTED,
    PAID,
    FULFILLED,
    CANCELLED    
}

State machine configuration:

@Log
@Configuration
@EnableStateMachineFactory
class SimpleEnumStatemachineConfiguration extends StateMachineConfigurerAdapter<OrderStates, OrderEvents> {

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
        transitions
                .withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID).event(OrderEvents.PAY)
                .and()
                .withExternal().source(OrderStates.PAID).target(OrderStates.FULFILLED).event(OrderEvents.FULFILL)
                .and()
                .withExternal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELLED).event(OrderEvents.CANCEL)
                .and()
                .withExternal().source(OrderStates.PAID).target(OrderStates.CANCELLED).event(OrderEvents.CANCEL);
    }

    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
        states
                .withStates()
                .initial(OrderStates.SUBMITTED)
                .state(OrderStates.PAID)
                .end(OrderStates.FULFILLED)
                .end(OrderStates.CANCELLED);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
        config.withConfiguration()
                .autoStartup(true)
    }
}

In my order service I call

StateMachine<OrderStates, OrderEvents> sm = this.factory.getStateMachine(orderIdKey);

But it seems that every time is new state machine created even for the same orderIdKey. So, how can I get access to state machine created when order was submitted on next state?

tyomka
  • 459
  • 2
  • 5
  • 14

1 Answers1

2

You have basically two options:

a) persist the state machine for the given orderId, using a state machine persister as explained here.

b) create a new state machine for given orderId (per HTTP request) and rehydrate the SM state based on the state of the order entity for the given orderId. SM objects are considered lightweight, so this is a viable approach as well. Below is a code sample:

StateMachine<Status, Event> build(long orderId) {
  orderService.getOrder(orderId) //returns Optional
  .map(order -> {
     StateMachine<Status, Event> sm = stateMachineFactory.getStateMachine(Long.toString(orderId));
     sm.stop();
     rehydrateState(sm, sm.getExtendedState, order.getStatus());
     sm.start();
     return sm;
   })
  .orElseGet(() -> createNewStateMachine(orderId);
}


void rehydrateState(StateMachine<Status, Event> newStateMachine, ExtendedState extendedState, Status orderStatus) {
  newStateMachine.getStateMachineAccessor().doWithAllRegions(sma ->
   sma.resetStateMachine(new DefaultStateMachineContext<>(orderStatus, null, null, extendedState));
  });
}
hovanessyan
  • 30,580
  • 6
  • 55
  • 83
  • thanks for your answer. Persisting state machine using state machine persister (a) looks complicated and full of different limitations. But using per request approach (b) seems it looses transitions between states. I mean that I can send any event and not necessary previous were executed, am I right? – tyomka Mar 04 '19 at 18:21
  • I consider your answer correct. Could you suggest what approach of persisting state machine would be best for clustered environment with at least two nodes? – tyomka Mar 04 '19 at 18:30
  • @tyomka https://docs.spring.io/spring-statemachine/docs/current/reference/#the-role-of-code-zookeeperstatemachinepersist-code – hovanessyan Jul 23 '20 at 13:25