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
implements StateProcessor, StateActionStep
{
@Override
public final ServiceResult
action(StateContext
context) throws Exception {
// data preparation
this.prepare(context);
// validation
ServiceResult
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
{
ServiceResult
check(StateContext
context);
default int order() { return 0; }
default boolean needRelease() { return false; }
default void release(StateContext
context, ServiceResult
result) {}
}Parallel execution example:
public
ServiceResult
parallelCheck(List
checkers, StateContext
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
{
@Override
public ServiceResult action(StateContext
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
sendEvent(OrderStateEvent event, FsmOrder order) throws Exception {
StateContext context = new StateContext(event, order);
StateProcessor
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.
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.