Backend Development 23 min read

Implementing Order Lifecycle Management with Spring Statemachine, Persistence, and AOP

This article explains the fundamentals of finite state machines, demonstrates how to model order states using Spring Statemachine, shows persistence options with in‑memory and Redis stores, and provides advanced techniques such as exception handling, extended state tracking, and AOP‑based result logging for reliable state transitions.

Architect
Architect
Architect
Implementing Order Lifecycle Management with Spring Statemachine, Persistence, and AOP

The article begins by introducing the concept of a state machine, describing how real‑world objects like an automatic door have discrete states (e.g., open and closed ) and how transitions are triggered by events.

It then outlines the four core concepts of a state machine: State, Event, Action, and Transition, and presents a simple finite‑state‑machine (FSM) model that can be visualized as a state‑transition diagram.

Next, the focus shifts to Spring Statemachine , a framework that brings state‑machine semantics to Spring applications. The framework offers a flat single‑level state machine, hierarchical state configurations, guards, actions, and integration with Spring IoC.

Quick start example shows the SQL table definition for an tb_order table and the Java enums representing order states and events:

public enum OrderStatus {
    WAIT_PAYMENT(1, "待支付"),
    WAIT_DELIVER(2, "待发货"),
    WAIT_RECEIVE(3, "待收货"),
    FINISH(4, "已完成");
    // getters and utility methods ...
}

public enum OrderStatusChangeEvent { PAYED, DELIVERY, RECEIVED; }

The state‑machine configuration is defined by extending StateMachineConfigurerAdapter :

@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter
{
    public void configure(StateMachineStateConfigurer
states) throws Exception {
        states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));
    }
    public void configure(StateMachineTransitionConfigurer
transitions) throws Exception {
        transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
            .and()
            .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
            .and()
            .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
    }
}

Persistence strategies are discussed: an in‑memory StateMachinePersister using a simple Map , and a Redis‑backed persister for distributed scenarios. Configuration snippets for Maven dependencies and application.yml are provided.

Service layer code shows how to start the state machine, restore its previous state, send events, and persist the new state. The sendEvent method is synchronized to guarantee thread safety and uses the extended state to record whether the business logic succeeded (1) or failed (0).

private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
    boolean result = false;
    try {
        orderStateMachine.start();
        stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
        Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
        result = orderStateMachine.sendEvent(message);
        if (!result) return false;
        Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
        orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition + order.getId());
        if (Objects.equals(1, o)) {
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } else {
            return false;
        }
    } catch (Exception e) {
        log.error("订单操作失败:{}", e);
    } finally {
        orderStateMachine.stop();
    }
    return result;
}

Listeners annotated with @OnTransition update the order record and optionally throw exceptions to simulate business failures. To capture the success/failure of these listeners, the article introduces a custom annotation @LogResult and an AOP aspect that records the outcome in the state machine’s extended state.

@Retention(RetentionPolicy.RUNTIME)
public @interface LogResult { String key(); }

@Aspect
@Component
public class LogResultAspect {
    @Around("logResultPointCut()")
    public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
        // invoke method, record 1 on success, 0 on exception
    }
}

Finally, the article lists testing steps (create order, pay, deliver, receive) and discusses common pitfalls such as the state machine swallowing exceptions, recommending storing exception flags in the extended state or using the provided aspect.

Supplementary sections contain promotional messages and related article links, which are not part of the technical content.

JavaAOPRedisSpringState MachinePersistenceOrder Management
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

login 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.