Introduction to Rule Engines and Their Application in a Car‑Buying Workflow
This article explains what a rule engine is, contrasts it with workflow engines, outlines its benefits, reviews popular Java rule‑engine frameworks, and demonstrates a practical implementation for a second‑hand car buying process using JSON configuration, Spring‑SpEL, and modular Java actions.
1. Rule Engine Introduction
Rule engines evolve from inference engines and are embedded components that separate business decisions from application code by using predefined semantic modules. They accept data input, interpret business rules, and make decisions based on those rules.
Difference between Rule Engine and Process Engine
Rule engines address mutable logic and business coupling, allowing hot‑updates and higher readability, whereas process engines manage multi‑role workflow transitions such as leave or approval processes.
Benefits of Rule Engines
Declarative programming : you describe *what* to do, not *how* to do it.
Separation of logic and data : business logic lives in rules, data stays in domain objects.
Knowledge centralization : rules form an executable knowledge base that is easy to read and document.
Human‑readable rules : if‑then syntax can be close to natural language, enabling non‑technical experts to understand them.
2. Rule Engine Frameworks
Common Java rule‑engine frameworks include Drools, Aviator, EasyRule, and QLExpress. The following images illustrate their comparative features:
3. Rule Engine in a Second‑Hand Car Buying Scenario
Background
The one‑stop car buying service requires multiple steps (viewing, confirming, etc.). Introducing a rule engine abstracts these steps, reduces code duplication, and improves maintainability.
Business Flow
Editing → Confirming → Starting view → Ending view → Completion.
Technical Implementation
The engine is built on EasyRule with two main parts: a reflection‑based rule flow and Spring‑SpEL condition evaluation.
/**
* 工作流程上下文
* @author mac
* @date 2020/10/26
*/
public class WorkFlowContext {
private BeanProvider beanProvider;
private Object requestSpec;
private Object responseSpec;
private String transitionName;
public String getTransitionName() { return transitionName; }
public WorkFlowContext setTransitionName(String transitionName) { this.transitionName = transitionName; return this; }
public BeanProvider getBeanProvider() { return beanProvider; }
public WorkFlowContext setBeanProvider(BeanProvider beanProvider) { this.beanProvider = beanProvider; return this; }
public
T getRequestSpec() { return (T) requestSpec; }
public WorkFlowContext setRequestSpec(Object requestSpec) { this.requestSpec = requestSpec; return this; }
public
T getResponseSpec() { return (T) responseSpec; }
public WorkFlowContext setResponseSpec(Object responseSpec) { this.responseSpec = responseSpec; return this; }
}Rule definitions are stored in a JSON file (e.g., buyCar.json ) that describes nodes, conditions, and transitions.
{
"name": "买车流程工作流模板",
"flow_id": "buyCar",
"init_data": {"status": 1, "action": 1},
"nodes": [
{"name": "payedNode", "desc": "已支付节点", "start": true, "condition": "status == 101", "transitions": [{"name": "CONFIRM_ORDER_ACTION", "to": "editorTakeOrderNode", "action": "confirmOrderAction"}]},
{"name": "editorTakeOrderNode", "desc": "顾问确认接单节点", "condition": "status == 130", "transitions": [{"name": "CONFIRM_CAR_ACTION", "to": "editorTakeCarNode", "action": "confirmCarAction"}]}
// ... other nodes omitted for brevity
]
}Base action classes provide common validation and parameter handling, while concrete actions (e.g., ConfirmCarAction ) implement specific business logic.
@Slf4j
@Component("confirmCarAction")
public class ConfirmCarAction extends BaseBuyCarAction {
@Override
public Result checkParam(CxmActionOperationVo request, ConsignmentCarOrderRecord record) {
Result superResult = super.checkParam(request, record);
if (superResult.isFailure()) return superResult;
if (!CarOrderRecordStatusEnum.TakeOrder.getValue().equals(record.getStatus())) {
return Result.createFailureResult("参数错误,不是顾问接单状态", CxmExceptionEnum.PARAM_ILLEGAL.getCode());
}
return Result.createSuccessResult();
}
@Override
public void buildParamAfter(CxmActionOperationVo request, ConsignmentCarOrderRecord record) {
record.setStatus(CarOrderRecordStatusEnum.TakeCar.getValue());
// build log entry ...
}
}The front‑end calls the engine via an HTTP endpoint, passing the action type.
@ApiOperation("确认接单")
@PostMapping("confirmOrder")
public Result confirmOrderAction(@Valid CxmActionOperationVo actionOperationVo) {
fillUserInfo(actionOperationVo);
actionOperationVo.setActionType(CxmBuyCarActionEnum.CONFIRM_ORDER_ACTION.getCode());
return actionExecuteService.actionExecute(actionOperationVo);
}4. Core Value
Centralized, readable rule definitions enable easier maintenance.
Configuration‑driven logic provides better business extensibility.
Hot‑update capability reduces deployment overhead.
Testing cost drops by ~50% because only action logic needs verification.
Uniform code structure supports agile iteration.
5. Summary
Introducing a rule engine separates complex business rules from code, lowers coupling, improves maintainability, and empowers both developers and business users to evolve functionality quickly and safely.
HomeTech
HomeTech tech sharing
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.