Backend Development 27 min read

State Machine Evaluation and Selection for Product Domain

The article analyzes DeWu’s product lifecycle, explains New, Product, and Draft types, introduces state‑machine fundamentals, compares Java enum, Spring, Squirrel, and Cola implementations, benchmarks their throughput (showing Cola vastly faster), and concludes that despite Cola’s performance, Spring StateMachine is chosen for its richer features and seamless Spring integration.

DeWu Technology
DeWu Technology
DeWu Technology
State Machine Evaluation and Selection for Product Domain

This article presents a comprehensive analysis of the product lifecycle at DeWu, describing three product types—New, Product, and Draft—and their state transitions. Only the Product type is sellable; New items become Products after review, while Drafts are editable copies generated during product updates.

The complex state flow of products (e.g., On‑Shelf, Off‑Shelf, Pending Review, etc.) motivates the introduction of a state machine to manage transitions reliably.

State Machine Fundamentals

A state machine consists of five core components: State, Event, Transition, Guard (condition), and Action. UML 2.4 defines simple, composite, and sub‑machine states, but for most business scenarios a simple state is sufficient.

Enum‑Based State Machine (Java)

public interface StateCondition {
    // Check if transition to target state is allowed
    boolean check(CommodityState target);
}

public interface StateAction {
    void doAction();
}

public enum CommodityState {
    TO_AUDIT {
        @Override
        StateCondition getCondition() {return new ToAuditStateCondition();}
        @Override
        StateAction getAction() {return new ToAuditStateAction();}
    },
    ON_SHELF {
        @Override
        StateCondition getCondition() {return new OnShelfStateCondition();}
        @Override
        StateAction getAction() {return new OnShelfStateAction();}
    },
    OFF_SHELF {
        @Override
        StateCondition getCondition() {return new OffShelfStateCondition();}
        @Override
        StateAction getAction() {return new OffShelfStateAction();}
    };

    boolean transition(CommodityState target) {
        StateCondition condition = getCondition();
        if (condition.check(target)) {
            StateAction action = getAction();
            action.doAction();
            return true;
        }
        throw new IllegalArgumentException("当前状态不符合流转条件");
    }
    abstract StateCondition getCondition();
    abstract StateAction getAction();
}

This enum implementation provides a thread‑safe, readable way to model simple state transitions.

Spring StateMachine

Spring StateMachine offers a full‑featured framework with configuration, persistence, guards, and actions. The core configuration class extends EnumStateMachineConfigurerAdapter and defines states, events, and transitions.

@Configuration
public class SpuStateMachineConfig extends EnumStateMachineConfigurerAdapter
{
    public static final String DEFAULT_MACHINEID = "spring/machine/default/machineid";
    // ... beans and persister definitions omitted for brevity ...
    @Override
    public void configure(StateMachineStateConfigurer
config) throws Exception {
        config.withStates()
            .initial(SpuStatesEnum.NONE)
            .states(EnumSet.allOf(SpuStatesEnum.class));
    }
    @Override
    public void configure(StateMachineTransitionConfigurer
transitions) throws Exception {
        transitions.withExternal()
            .source(SpuStatesEnum.INIT)
            .target(SpuStatesEnum.DRAFT)
            .event(SpuEventsEnum.CREATE_DRAFT)
            .guard(defaultSpuGuard)
            .action(spuCreateDraftSuccessAction, defaultSpuErrorAction)
            // additional transitions omitted ...
            ;
    }
}

The service class SpuStateMachineService creates a Spring context, obtains the factory, and sends events to the machine.

public class SpuStateMachineService {
    private final ApplicationContext applicationContext;
    private final StateMachineFactory
spuStateMachineFactory;
    private final StateMachinePersister
spuStateMachinePersister;
    public SpuStateMachineService(String machineId) {
        applicationContext = new AnnotationConfigApplicationContext(SpuStateMachineConfig.class);
        spuStateMachineFactory = applicationContext.getBean(StateMachineFactory.class);
        spuStateMachinePersister = applicationContext.getBean(StateMachinePersister.class);
    }
    public boolean sendEvent(SpuEventsEnum event, SpuMessageContext context) {
        StateMachine
stateMachine =
            spuStateMachineFactory.getStateMachine(SpuStateMachineConfig.DEFAULT_MACHINEID);
        try {
            spuStateMachinePersister.restore(stateMachine, context);
            Message
message = MessageBuilder.withPayload(event)
                .setHeader("request", context)
                .build();
            boolean success = stateMachine.sendEvent(message);
            if (success) {
                spuStateMachinePersister.persist(stateMachine, context);
            }
            return success;
        } finally {
            stateMachine.stop();
        }
    }
}

Squirrel StateMachine

Squirrel provides a lightweight DSL‑based builder.

StateMachineBuilder
builder = StateMachineBuilderFactory.create();
builder.externalTransition()
    .from(SpuStateEnum.INIT)
    .to(SpuStateEnum.DRAFT)
    .on(SpuEventEnum.CREATE_DRAFT)
    .when(SpuEventEnum.CREATE_DRAFT.getCondition())
    .perform(SpuEventEnum.CREATE_DRAFT.getAction());
// other transitions omitted ...

Cola StateMachine

Cola focuses on stateless design, using a fluent API to define transitions.

StateMachineBuilder
builder = StateMachineBuilderFactory.create();
builder.externalTransition()
    .from(SpuStateEnum.INIT)
    .to(SpuStateEnum.DRAFT)
    .on(SpuEventEnum.CREATE_DRAFT)
    .when(SpuEventEnum.CREATE_DRAFT.getCondition())
    .perform(SpuEventEnum.CREATE_DRAFT.getAction());
// additional transitions follow the same pattern

Performance Benchmark

JMH benchmarks were written to compare throughput of Spring StateMachine and Cola StateMachine. The benchmark runs with 2 warm‑up iterations, 2 measurement iterations, 8 threads, and 2 forks.

@Warmup(iterations = 2)
@BenchmarkMode({Mode.Throughput})
@Measurement(iterations = 2, time = 1)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 2)
@Threads(8)
@State(Scope.Benchmark)
public class SpringStateMachineBench {
    private SpuStateMachineService stateMachineService;
    @Setup
    public void prepare() {
        stateMachineService = new SpuStateMachineService("commodity-machine");
    }
    @Benchmark
    public void test_sendEvent() {
        SpuMessageContext ctx = new SpuMessageContext("122312", "spu-1222", "https://example.com/video.mp4");
        stateMachineService.sendEvent(SpuEventsEnum.CREATE_SPU, ctx);
    }
}

Result for Spring StateMachine:

Result "SpringStateMachineBench.test_sendEvent":
  35.549 ±(99.9%) 27.813 ops/ms [Average]
  (min, avg, max) = (31.712, 35.549, 40.171), stdev = 4.304

Result for Cola StateMachine:

Result "ColaStateMachineBench.test_sendEvent":
  50724.516 ±(99.9%) 31836.478 ops/ms [Average]
  (min, avg, max) = (44157.738, 50724.516, 55205.836), stdev = 4926.730

The benchmark shows Cola StateMachine achieving roughly 1,449× higher throughput than Spring StateMachine when the actions are empty.

Conclusion

Although Cola offers superior raw performance, the product domain prioritises rich feature support, complex business logic handling, and seamless integration with the Spring ecosystem. Therefore, Spring StateMachine was selected as the final framework for managing product state transitions.

JavaSpringState MachineCOLAenumPerformance BenchmarkSquirrel
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

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.