7

I'm trying to use Spring State Machine in my project, because there is an object is Order with several state

  • NEW
  • PAID
  • PACKAGED
  • DELIVERED
  • CANCELED
  • RETURNED

That object could be implement as following

public class Order {
  private String id;
  private Customer customer;
  private OrderState state;
// Some other fields with getter and setter
}

and I introduced OrderService, get order from DB, set some information including OrderState, and then save to DB.

However I don't know how to apply Spring state machine into this one, is it too complex to use with simple purpose like that?

Khoi Nguyen
  • 1,072
  • 2
  • 15
  • 32
  • *simple purpose like that*-- like what? You didn't describe how transitions between states should be handled, which transitions are allowed etc. If you just store the state within the `Order`, simple enum is quite enough. – Alex Salauyou Mar 10 '16 at 09:14
  • From Spring state machine document, I would have to implement a StateChange Listener, and then get an order from DB, set state and then save back to DB. All these steps will be done by the Listener class instead of OrderService. I don't know if we have another way for implementation – Khoi Nguyen Mar 10 '16 at 09:20
  • see example https://github.com/spring-projects/spring-statemachine/tree/master/spring-statemachine-samples/ordershipping/src – Diluka W Jul 09 '18 at 02:44

1 Answers1

8

Define your states

public enum OrederStates {
   NEW, PAID, PACKAGED; // etc
}

Then define your events

public enum OrderEvents {
    PAYMENT, PACK, DELIVER; // etc 
}

Then declare your event listeners

@WithStateMachine
public class OrderEventHandler {
    @Autowired
    OrderService orderService;

    @OnTransition(src= "NEW",target = "PAID")
    handlePayment() {
       // Your code orderService.*
       // ..   
    }
    // Other handlers
}

Now configure your application to to use state machine

@Configuration
@EnableStateMachine
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {

    @Override
    public void configure(StateMachineStateConfigurer<States, Events> states)
            throws Exception {
        states
            .withStates()
                .initial(OrderStates.NEW)
                .states(EnumSet.allOf(OrderStates.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
            throws Exception {
        transitions
            .withExternal()
                .source(OrderStates.NEW).target(OrderStates.PAID)
                .event(OrderEvents.PAYMENT)
                .and()
            .withExternal()
                .source(OrderStates.PAID).target(OrderStates.PACKED)
                .event(OrderEvents.PACK);
    }
}

And finally use it in your Application/Controller

public class MyApp {

    @Autowired
    StateMachine<States, Events> stateMachine;

    void doSignals() {
        stateMachine.start();
        stateMachine.sendEvent(OrderEvents.PAYMENT);
        stateMachine.sendEvent(Events.PACK);
    }
 }

Use this guide for getting started with state machine and this referce to learn more.

mirmdasif
  • 6,014
  • 2
  • 22
  • 28
  • 1
    Thanks for your suggestions, I'm developing a REST service that handling these orders, the parameter for 1 service could has 2 parameters: orderId and event. So I have to get a particular order from that orderId parameter, and change the state to target one, by that given event, I don't know how to do that with your implementation there. Please help to check – Khoi Nguyen Mar 10 '16 at 11:11
  • 1
    handlePayment should be public according exception I am getting. – Peter Mar 11 '16 at 15:28
  • 6
    A very nice summary, however I'm not sure it addresses the actual use-case - changing the state of a particular order. – mysomic Jun 03 '18 at 17:30
  • I have a similar scenario, the handlePayment() function need to use some inputs from Order object that received at the endpoint. Any thoughts on that ? – Davy Jones May 20 '20 at 04:36