Mastering State Patterns with Spring State Machine: From Theory to Order Workflow

This article explains the classic State design pattern, demonstrates a traffic‑light example, compares Spring State Machine with COLA, and walks through building a complete order‑status workflow in Spring Boot using enums, configuration, listeners, services, and a test controller, while highlighting trade‑offs and alternatives.

Architect
Architect
Architect
Mastering State Patterns with Spring State Machine: From Theory to Order Workflow

The article begins by recalling the State design pattern, which encapsulates an object's behavior in separate state classes so that the object's actions change automatically when its internal state changes. It defines the pattern, lists its three roles—Context, State, and ConcreteState—and shows a UML class diagram (illustrated by an image).

To illustrate the pattern, a simple traffic‑light example is provided. The abstract state class MyState declares an abstract handler() method. Two concrete states, RedLightState and GreenLightState, implement handler() to print "红灯停" and "绿灯行" respectively. A MyContext holds a MyState instance and delegates handler() calls to the current state. The test class creates a context, switches between red and green states, and prints the expected output, demonstrating how the pattern eliminates explicit if‑else logic.

After establishing the pattern, the article introduces the concept of a state machine (a mathematical model of states and transitions) and explains that Spring State Machine is an implementation of the State pattern with richer features. It outlines the essential elements of a state machine: current state, trigger event, response function, and target state.

A comparison table (described in text) contrasts Spring State Machine with COLA State Machine, noting that Spring offers a richer ecosystem (284 interfaces/classes, reactive API) but is harder to customize, whereas COLA is simpler with fewer interfaces (36) and easier customization.

Next, the article walks through building an order‑status workflow using Spring State Machine:

1. Dependency

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>${springboot.version}</version>
</dependency>

2. Domain Model

public class Order {
    private Long orderId;
    private OrderStatusEnum orderStatus;
}

public enum OrderStatusEnum { WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH }

public enum OrderStatusChangeEventEnum { PAYED, DELIVERY, RECEIVED }

3. State Machine Configuration

@Configuration
@EnableStateMachine
public class OrderStatusMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum> {
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> states) throws Exception {
        states.withStates()
            .initial(OrderStatusEnum.WAIT_PAYMENT)
            .end(OrderStatusEnum.FINISH)
            .states(EnumSet.allOf(OrderStatusEnum.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> transitions) throws Exception {
        transitions.withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER).event(OrderStatusChangeEventEnum.PAYED)
            .and()
            .withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE).event(OrderStatusChangeEventEnum.DELIVERY)
            .and()
            .withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH).event(OrderStatusChangeEventEnum.RECEIVED);
    }
}

4. Listener

@Component
@WithStateMachine
@Transactional
public class OrderStatusListener {
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setOrderStatus(OrderStatusEnum.WAIT_DELIVER);
        System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
    // similar methods for deliverTransition and receiveTransition omitted for brevity
}

5. Service Layer

@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private StateMachine<OrderStatusEnum, OrderStatusChangeEventEnum> orderStateMachine;
    private long id = 1L;
    private Map<Long, Order> orders = Maps.newConcurrentMap();

    public Order create() {
        Order order = new Order();
        order.setOrderStatus(OrderStatusEnum.WAIT_PAYMENT);
        order.setOrderId(id++);
        orders.put(order.getOrderId(), order);
        System.out.println("订单创建成功:" + order);
        return order;
    }

    public Order pay(long orderId) {
        Order order = orders.get(orderId);
        Message<OrderStatusChangeEventEnum> message = MessageBuilder.withPayload(OrderStatusChangeEventEnum.PAYED)
            .setHeader("order", order).build();
        if (!sendEvent(message)) {
            System.out.println("支付失败, 状态异常,订单号:" + orderId);
        }
        return orders.get(orderId);
    }
    // deliver and receive methods follow the same pattern

    private synchronized boolean sendEvent(Message<OrderStatusChangeEventEnum> message) {
        boolean result = false;
        try {
            orderStateMachine.start();
            result = orderStateMachine.sendEvent(message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(message)) {
                Order order = (Order) message.getHeaders().get("order");
                if (Objects.nonNull(order) && Objects.equals(order.getOrderStatus(), OrderStatusEnum.FINISH)) {
                    orderStateMachine.stop();
                }
            }
        }
        return result;
    }
}

6. Controller (Test Entry)

@RestController
public class OrderController {
    @Resource
    private OrderService orderService;

    @RequestMapping("/testOrderStatusChange")
    public String testOrderStatusChange() {
        orderService.create();
        orderService.create();
        orderService.pay(1L);
        orderService.deliver(1L);
        orderService.receive(1L);
        orderService.pay(2L);
        orderService.deliver(2L);
        orderService.receive(2L);
        System.out.println("全部订单状态:" + orderService.getOrders());
        return "success";
    }
}

The execution results (shown in two screenshots) confirm that the state machine correctly drives the order through WAIT_PAYMENT → WAIT_DELIVER → WAIT_RECEIVE → FINISH, and that invalid transitions are rejected.

7. Reflection and Alternatives

The author reflects on the characteristics of state machines and suggests three alternative implementation ideas: using a message‑queue to publish events, a scheduled job that polls orders and advances them based on business rules, and a rule‑engine that encodes state‑transition logic.

Finally, the article summarizes that the State pattern provides a clean way to encapsulate state‑dependent behavior, Spring State Machine builds on this pattern with a rich API, and the presented order‑workflow example demonstrates a practical application. Readers are invited to comment on other possible approaches.

State pattern class diagram
State pattern class diagram
Spring StateMachine execution result
Spring StateMachine execution result
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaspringstate machinedesign patternState PatternOrder Workflow
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.