How to Build a Scalable Multi‑State Order Processing Engine with State‑Machine Patterns

This article explains how to design a robust order‑status flow engine for transaction systems by applying state‑machine and strategy patterns, separating business logic vertically and horizontally, using annotations, context objects, plug‑in extensions, and ensuring scalability, maintainability, and message‑database consistency.

ITFLY8 Architecture Home
ITFLY8 Architecture Home
ITFLY8 Architecture Home
How to Build a Scalable Multi‑State Order Processing Engine with State‑Machine Patterns

Introduction: Order status flow is the core work of a transaction system. Order systems often have many states, long chains, complex logic, and multiple scenarios, types, and business dimensions. Ensuring stability while achieving scalability and maintainability is a key challenge.

image.png
image.png

1 Background

Order status flow is the most critical work of a transaction system, featuring many states, long chains, and complex logic, as well as multi‑scenario, multi‑type, and multi‑business‑dimension characteristics. Under the premise of stable status flow, scalability and maintainability must be addressed.

Taking the Gaode ride‑hailing order status as an example, statuses include passenger order, driver acceptance, driver arrival, trip start, trip end, fee confirmation, payment success, order cancellation, order closure, etc. Order types include premium, fast, taxi, and each type may have sub‑types. Business scenarios include airport pickup, corporate rides, inter‑city carpool, etc.

When status, type, scenario, and other dimensions are combined, each combination may have different processing logic, making a massive if‑else structure impossible. This article discusses solutions for handling "multiple states + multiple types + multiple scenarios + multiple dimensions" while keeping the system extensible and maintainable.

2 Implementation方案

To solve the complex order‑status flow, we design from vertical and horizontal dimensions. Vertically we focus on business isolation and process orchestration; horizontally we focus on logic reuse and business extension.

1 Vertical: Business Isolation and Process Orchestration

Application of State Pattern

For multi‑state or multi‑dimension business logic, we use the State or Strategy pattern. The core idea is "divide and conquer": define a base logic interface, let each state or type implement it, and invoke the corresponding implementation based on the current state or type, achieving independent and isolated code.

This not only improves extensibility and maintainability but also serves as a basic means to reduce impact scope, similar to gray‑release or batch deployment.
image.png
image.png
/**
 * 状态机处理器接口
 */
public interface StateProcessor {
    /**
     * 执行状态迁移的入口
     */
    void action(StateContext context) throws Exception;
}

For a single state or type the above method works. For the "multiple states + multiple types + multiple scenarios + multiple dimensions" combination, we annotate implementations with @OrderProcessor, specifying state, bizCode, and sceneId, and let the engine select the appropriate processor at runtime.

Because Java enums cannot be inherited, we use String fields for extensible attributes in annotations.

image.png
image.png
/**
 * 状态机处理器注解标识
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface OrderProcessor {
    String[] state() default {};
    String[] bizCode() default {};
    String[] sceneId() default {};
}

When multiple dimensions are combined, we treat the combination as an "event" and use event‑driven state transition: a specific event triggers the next state based on current state and context.

image.png
image.png

2 Horizontal: Logic Reuse and Business Extension

After establishing vertical isolation, we still encounter repeated logic across different types or scenarios (e.g., coupon verification, invoice calculation). We solve this by two plugin‑based approaches:

Standard flow + differential plugins : Define a default processing flow; write plugins for the small differences.

Common plugins for shared logic : Encapsulate common code as plugins that multiple processors can load.

image.png
image.png
/**
 * 插件注解
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface ProcessorPlugin {
    String[] state() default {};
    String event();
    String[] bizCode() default {};
    String[] sceneId() default {};
}

3 State‑Machine Engine Execution Process

Initialization Phase : All classes annotated with @OrderProcessor are registered as Spring beans. A BeanPostProcessor builds a three‑level map: state → event → bizCode@sceneId → list of processors.

public class DefaultStateProcessRegistry implements BeanPostProcessor {
    private static Map<String, Map<String, Map<String, List<AbstractStateProcessor>>>> stateProcessMap = new ConcurrentHashMap<>();
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AbstractStateProcessor && bean.getClass().isAnnotationPresent(OrderProcessor.class)) {
            OrderProcessor annotation = bean.getClass().getAnnotation(OrderProcessor.class);
            initProcessMap(annotation.state(), annotation.event(), annotation.bizCode(), annotation.sceneId(), stateProcessMap, (AbstractStateProcessor) bean);
        }
        return bean;
    }
    // ... registration logic omitted for brevity ...
}

Runtime Phase : The engine receives an OrderStateEvent, builds a StateContext, looks up the appropriate processor from the map, and invokes its action method. The abstract processor defines a template method that executes six steps: prepare → check → getNextState → action → save → after.

public abstract class AbstractStateProcessor<T, C> implements StateProcessor<T>, StateActionStep<T, C> {
    @Override
    public final ServiceResult<T> action(StateContext<C> context) throws Exception {
        // prepare
        this.prepare(context);
        // check
        ServiceResult<T> result = this.check(context);
        if (!result.isSuccess()) return result;
        // next state
        String nextState = this.getNextState(context);
        // business logic
        result = this.action(nextState, context);
        if (!result.isSuccess()) return result;
        // persist
        result = this.save(nextState, context);
        if (!result.isSuccess()) return result;
        // after
        this.after(context);
        return result;
    }
}

Checkers are split into param, sync, and async groups. Param checkers run first; sync checkers run serially; async checkers run in parallel using a thread pool, preserving order via a sorted list and Futures.

public class CheckerExecutor {
    public <T, C> ServiceResult<T> parallelCheck(List<Checker> checkers, StateContext<C> context) {
        if (CollectionUtils.isEmpty(checkers)) return new ServiceResult<>();
        if (checkers.size() == 1) return checkers.get(0).check(context);
        List<Future<ServiceResult>> futures = new ArrayList<>(checkers.size());
        checkers.sort(Comparator.comparingInt(Checker::order));
        for (Checker c : checkers) {
            futures.add(executor.submit(() -> c.check(context)));
        }
        for (Future<ServiceResult> f : futures) {
            ServiceResult sr = f.get();
            if (!sr.isSuccess()) return sr;
        }
        return new ServiceResult<>();
    }
}

Message consistency with database updates is handled via two‑phase commit, transactional outbox, or compensation mechanisms, ensuring that state changes and downstream messages stay in sync.

image.png
image.png

4 Summary

The presented state‑machine engine demonstrates a practical approach to handling complex, multi‑dimensional order workflows. By separating concerns vertically (business isolation, process orchestration) and horizontally (logic reuse via plugins or inheritance), the system achieves high scalability, maintainability, and extensibility.

image.png
image.png
image.png
image.png
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.

Design Patternsstate machineorder processing
ITFLY8 Architecture Home
Written by

ITFLY8 Architecture Home

ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.

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.