Design and Implementation of a Scalable Order State Machine for Complex Multi‑Dimensional Order Processing
The article presents a scalable, two‑dimensional order state machine architecture that separates vertical business isolation via annotated StateProcessor classes from horizontal logic reuse through modular checkers and plugins, employing distributed locks, optimistic DB locking, and a registry to ensure extensible, consistent multi‑dimensional order processing.
Background : Order status flow in transaction systems often involves many states, types, scenes, and business dimensions. Maintaining stability while ensuring scalability and maintainability is a core challenge.
Implementation Overview : The solution is split into two dimensions. The vertical dimension addresses business isolation and process orchestration, while the horizontal dimension focuses on logic reuse and business extension.
1. Vertical Solution – State Pattern : Each order state is handled by a separate processor implementing a common StateProcessor interface. Processors are marked with @OrderProcessor to declare the state, business code, and scene they handle.
public interface StateProcessor {
void action(StateContext context) throws Exception;
}Example processor annotation:
@OrderProcessor(state = "INIT", bizCode = {"CHEAP", "POPULAR"}, sceneId = "H5")
public class StateCreateProcessor implements StateProcessor {
// implementation
}The abstract base class AbstractStateProcessor provides a template method that sequences the workflow: prepare → check → getNextState → action → save → after.
public abstract class AbstractStateProcessor<T, C> implements StateProcessor, StateActionStep<T, C> {
@Override
public final ServiceResult<T> action(StateContext<C> context) throws Exception {
// data preparation
this.prepare(context);
// validation
ServiceResult<T> result = this.check(context);
if (!result.isSuccess()) return result;
// determine next state
String nextState = this.getNextState(context);
// business logic
result = this.action(nextState, context);
if (!result.isSuccess()) return result;
// persistence
result = this.save(nextState, context);
if (!result.isSuccess()) return result;
// post‑processing
this.after(context);
return result;
}
}2. Horizontal Solution – Checkers and Plugins :
• Checkers are small validation components that can be executed serially or in parallel. They are grouped into parameter checkers, synchronous checkers, and asynchronous checkers. The CheckerExecutor runs them and preserves order when needed.
public interface Checker<T, C> {
ServiceResult<T> check(StateContext<C> context);
default int order() { return 0; }
default boolean needRelease() { return false; }
default void release(StateContext<C> context, ServiceResult<T> result) {}
}Parallel execution example:
public <T, C> ServiceResult<T, C> parallelCheck(List<Checker> checkers, StateContext<C> context) {
if (checkers.size() == 1) return checkers.get(0).check(context);
// submit each checker to an executor and collect results preserving order
// ...
}• Plugins allow insertion of custom logic before or after the main action and save steps. They are annotated with @ProcessorPlugin and implement PluginHandler.
@ProcessorPlugin(state = OrderStateEnum.INIT, event = OrderEventEnum.CREATE, bizCode = "BUSINESS")
public class EstimatePricePlugin implements PluginHandler<String, CreateOrderContext> {
@Override
public ServiceResult action(StateContext<CreateOrderContext> context) throws Exception {
String price = ""; // call price service
context.getContext().setEstimatePriceInfo(price);
return new ServiceResult();
}
}The engine executes plugins between the action and save phases:
result = this.action(nextState, context);
if (!result.isSuccess()) return result;
// execute plugins
this.pluginExecutor.parallelExecutor(context);
result = this.save(nextState, context);
if (!result.isSuccess()) return result;3. Concurrency and Consistency : To prevent concurrent state transitions, the engine acquires a distributed lock (e.g., Redis) per order and uses optimistic locking at the database level. Message consistency between DB updates and event publishing is handled via two‑phase commit (transactional messages) or a reliable outbox table with periodic retry.
4. Registry and Execution Flow : During Spring initialization, all beans annotated with @OrderProcessor are registered in a three‑level map keyed by state → event → bizCode@sceneId. At runtime, OrderFsmEngine.sendEvent builds a StateContext, looks up the appropriate processor, and invokes its template method.
public ServiceResult<T> sendEvent(OrderStateEvent event, FsmOrder order) throws Exception {
StateContext context = new StateContext(event, order);
StateProcessor<T> processor = registry.acquireStateProcess(order.getOrderState(), event.getEventType(), order.bizCode(), order.sceneId());
return processor.action(context);
}The design supports extensibility via inheritance (subclassing processors) or plugin composition, enabling reuse of common logic across many state‑type‑scene combinations.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Amap Tech
Official Amap technology account showcasing all of Amap's technical innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
