How Lalamove Replaced Scattered if/else Logic with a Visual Rule Engine
Complex Business Rules Too Many? We Built a Rule Visualization Configuration Platform
1. Why Such a Platform Is Needed
In business systems, the parts that change frequently and whose complexity easily spirals out of control are often not the underlying capabilities, but the various business rules.
For example, whether invoicing is allowed, whether unbinding is allowed, whether address editing is allowed, how to choose business switches, how to adjust risk control hit strategies, etc. These rules usually share several common characteristics:
- Many judgment conditions, prone to continuous change along dimensions like city, vehicle type, business line, channel, and order status.
- Rules change frequently, but directly modifying code has a long release cycle, and directly modifying the configuration center lacks structural constraints.
- Difficult to verify before changes, difficult to compare after changes, and difficult to trace and roll back when problems occur.
- Rule owners, approvers, impact scope, and release process lack unified platform-based management and control.
In the past, many complex business logics were deposited as code branches or configuration center entries. Code branches are stable but lack flexibility, while configuration center changes are flexible but have limited capabilities in rule expression, verification, and change management.
The core problem that the rule visualization configuration platform aims to solve is to abstract "business judgment logic scattered in code and configuration" into visual, verifiable, approvable, grayscale-capable, and traceable policy assets.
Typical Problems
| Problem | Impact | Platform-based Solution |
|---|---|---|
| Configuration changes not notified to the owner | Related business parties cannot perceive risks in advance | Release approval, owner notification, release card |
| Free-form configuration structure, easy to write incorrectly | Online rules do not meet expectations, potentially causing financial loss or public opinion issues | Visual orchestration, field type constraints, test case verification |
| High regression cost | Requires constructing real business links to verify | Directly construct input parameters on the page, execute rule chains offline |
| Lack of small-traffic verification before going live | High risk of one-time full rollout | Dual-run comparison, proportional grayscale traffic switching |
| Changes are not traceable | Difficult problem locating and rollback | Snapshots, release records, approval records, rollback capability |
2. What It Is
The rule visualization configuration platform is a set of "rule configuration, policy orchestration, release governance, and runtime execution platform" oriented towards multiple business domains.
It breaks down logic originally written in code, such as if/else, switch, policy selection, and result output, into reusable fields, components, and chains. Business personnel or developers can visually orchestrate rules on the management side, and the platform then publishes the rules as machine-executable rule chains, delivering them to business service runtimes for execution.
It can be understood from four levels:
- Fields/Options: Business variables usable within rules, such as order fields, waybill fields, billing fields, business input parameters, and intermediate results.
- Components: An independent business judgment or result output, similar to a method in code.
- Chains: A complete rule logic composed of multiple components orchestrated according to EL expressions, similar to a business process composed of multiple methods.
- Runtime: After the business service introduces the SDK, it executes rules based on the chain ID and input parameters to obtain structured results.
It is not simply a configuration page, nor is it a rule service that all requests must call remotely. More precisely, it consists of four layers of capabilities:
| Layer | Role | Responsibility |
|---|---|---|
| Management Layer | Rule Management Console | Rule spaces, fields, components, chains, test cases, approval, release, comparison, traffic switching, rollback |
| Configuration Service Layer | Configuration Center Server | Rule configuration persistence, publishing to the configuration center, providing batch queries |
| Configuration Consumption Layer | Configuration Consumption SDK | Business service startup pulls configuration, local caching, listens for changes, notifies runtime |
| Runtime Layer | Rule Execution SDK | Parses rule chains, registers execution models, hot updates, executes rules, supports comparison and grayscale |
The overall main link is:
Management console configures rules
-> Approved and published
-> Configuration server persists and pushes configuration changes
-> Configuration consumption SDK pulls and listens for configuration within the business service
-> Rule execution SDK loads rule chains
-> Business service calls SDK to execute rules
With this basic definition, looking back at the initial problem, the platform's value becomes clearer: it is not simply adding a management page for configuration, but filling the gaps in expression, verification, release, and runtime governance for complex rules.
3. What Problems It Solves
1. Upgrading from "Configuration Strings" to "Structured Rules"
Configuration centers are better suited for storing simple configurations, but complex strategies often require conditions, branches, results, nested chains, and field types. The rule visualization configuration platform abstracts these contents into spaces, categories, options, components, chains, and test cases, giving business strategies structured expression capabilities.
| Capability | Ordinary Configuration Center | Rule Visualization Configuration Platform |
|---|---|---|
| Expression Method | String / JSON | Fields + Components + Chains + EL |
| Complex Branching | Relies on manual JSON maintenance | Visual orchestration |
| Change Verification | Usually relies on business regression | Page test cases execute directly |
| Release Control | Basic approval and notification | Owner approval, change details, release snapshots |
| Grayscale Capability | Relies on business self-implementation | Dual-run comparison, proportional traffic switching |
| Rollback & Traceability | Relies on configuration history | Chain snapshots, release snapshots, rollback |
| Domain Isolation | Coarse namespace dimension | Spaces, permission groups, business categories |
2. Reducing Business Change Costs
For frequently adjusted business strategies, it is no longer required to modify code, release versions, and wait for deployment each time. Rule maintainers can adjust conditions and output results on the page, while R&D mainly focuses on depositing reusable fields, components, and runtime capabilities.
3. Reducing Online Risks
Before rule release, test case verification is required. During release, it goes through approval. After release, dual-run observation of differences can be performed first, followed by small-traffic switching through proportional control. When problems occur, quick rollback can be performed based on release snapshots.
4. Enhancing Rule Reuse and Accumulation
Business fields like orders, waybills, and bills can be maintained uniformly. Components can be reused, and chains can reference sub-chains. As implementation scenarios increase, the platform will gradually accumulate into a cross-business-domain policy asset library.
Behind these benefits corresponds a set of essential foundational capabilities. That is, "visual configuration" alone is insufficient to support complete rule governance; the platform also needs to incorporate verification, approval, grayscale, rollback, and asset reuse into its design.
4. Core Capabilities the Platform Needs
If a rule platform only solves "putting rules on a page for configuration," its value is actually limited. The more core questions are: how rules are expressed, how they are verified, how they are published, how they take effect safely online, and how to quickly locate and roll back when problems occur.
Therefore, the platform's core capabilities can be broken down into several categories:
1. Visual Orchestration
The platform breaks rules down into fields, components, and chains. Fields are responsible for describing the data rules can use, components carry independent judgment or output logic, and chains are responsible for combining multiple components in sequence, conditions, and branches.
In this way, complex rules are no longer an incomprehensible JSON or a pile of scattered configuration items, but become a structured process that can be read, reused, and reviewed.
2. Test Case Verification
Rules must be verifiable before going live. The platform supports directly constructing input parameters on the page and executing rule chains, without needing to go through a complete real business interface. For complex hit conditions, test cases can be accumulated as part of the rule assets and reused for every subsequent change.
3. Release Approval and Change Traceability
Although rules are configurations, their impact is no less than code. The platform retains an approval process before release, displaying change details during approval; after release, it retains chain snapshots and release snapshots. This controls change risk and allows quick identification of "who changed what and when" after a problem occurs.
4. Dual-Run Comparison and Grayscale Traffic Switching
For high-risk rules, the platform supports dual-running old and new rules. During the dual-run phase, the online main chain and the to-be-published chain can be executed simultaneously, only observing result differences without affecting actual business results. After confirming the differences meet expectations, traffic is gradually switched to the new rule through proportional control.
5. Quick Rollback
Each release generates a release snapshot. When problems occur, the previous version can be restored based on the historical snapshot and re-synchronized to the configuration center. Compared to manual configuration rollback, snapshot-based rollback is more reliable and more suitable for complex chains.
6. Field and Component Assetization
As more scenarios are connected, business fields, judgment components, and result components will gradually accumulate. When configuring new rules later, there is no need to start from scratch each time; existing fields and components can be reused, and new chains can be orchestrated.
The capability list clarifies the problems the platform aims to solve, but during actual implementation, it is necessary to further answer "how these capabilities are split, deployed, and run." This leads to the platform's core design.
5. Core Design Ideas
This section does not expand on code details first, but looks at several key design trade-offs. They determine whether the platform can subsequently support complex rules, frequent changes, online grayscale, and local high-performance execution.
1. Separation of Management Plane and Runtime Plane
The rule visualization configuration platform does not build all capabilities into one large service, but splits them into the management plane, configuration plane, and runtime plane:
- Management Plane: Responsible for rule asset management, test cases, approval, release, rollback, and permission control.
- Configuration Plane: Responsible for rule configuration persistence, version management, configuration push, client pull, local caching, and change notification.
- Runtime Plane: Embedded into business services in the form of an SDK to execute rules locally, without needing to remotely call the rule center for each execution.
The benefit of this design is: the rule execution link is short and performance is stable; the rule release link can reuse the existing configuration center and approval system.
2. Using Chain JSON as the Rule Release Artifact
The management console saves edit-state models, including rule_chain, rule_component, rule_chain_snapshot, rule_chain_publish_snapshot, etc. During release, the management console assembles information such as chains, components, EL, parameter types, and grayscale ratios into a Chain JSON recognizable by the SDK.
A fixed convention is used when publishing to the configuration center:
module = rules
key = chainKey
value = Chain JSON
Business services only need to configure the chainKeys they care about, and the runtime SDK will automatically pull the corresponding rules from the configuration center.
3. Using LiteFlow as the Execution Kernel
The runtime of the rule visualization configuration platform does not reinvent a process executor but completes chain execution based on LiteFlow. The SDK is responsible for converting the Chain and Component published by the management console into Chains, EL, and Script Nodes recognizable by LiteFlow.
Core runtime objects include:
ChainHelper: Responsible for loading, reloading, unloading, and local caching of rule chains and script components.RuleConfigParser: Responsible for reading rules from the configuration cache, listening for changes, and triggering hot updates.ChainExecutor: The business execution entry point, responsible for main chain execution, comparison chain dual-run, proportional traffic switching, and result encapsulation.ChainContext: Rule execution context, providing the current chain, input parameters, intermediate calculation results, etc.ChainResultContext: Rule result context, through which script components write the final output.
4. Pre-release Verification, In-release Approval, Post-release Grayscale
The risk of rule changes mainly comes from "configuration correctness" and "online impact scope." Therefore, risk control is designed to be split into three stages:
| Stage | Control Method | Purpose |
|---|---|---|
| Pre-release | Test cases, script validation, chain execution validation | Confirm rule logic meets expectations |
| In-release | Owner approval, change details, permission control | Confirm changes are reviewed by the correct person |
| Post-release | Dual-run comparison, proportional traffic switching, release notification, rollback | Small-traffic verification, quick stop-loss when problems found |
5. Supporting Hot Updates but Retaining Fallback Synchronization
After rule configuration is published, the configuration server writes to the database and pushes configuration changes. The configuration consumption SDK within the business service listens for configuration center changes on one hand, and periodically pulls configurations as a fallback on the other, avoiding configuration inconsistencies caused by relying solely on push.
The hot update path is as follows:
Configuration center change
-> Configuration listener
-> Local configuration repository updates cache
-> Sends change event
-> Rule parser detects change
-> ChainHelper reload / unload
-> LiteFlow FlowBus updates rule chain
The above discusses design trade-offs. Next, we go one level deeper to see how these designs collaborate within the system: how rules flow from the management console to the runtime, how models are transformed, and ultimately how they are executed through LiteFlow.
6. Core Principles
The previous sections discussed why the platform is needed and what capabilities it provides. Entering the implementation level, the core problems can be broken down into three:
- How rule configurations flow between modules: Which modules are responsible for editing, publishing, caching, and execution respectively.
- How rules are expressed within the system: What system models do the spaces, options, components, and chains on the page ultimately correspond to.
- How rules are actually executed: How the configuration model is converted into an executable process, and how business requests get results after entering.
This section unfolds along three main lines: 'Architecture & Models → Publishing & Governance → Pulling, Compilation & Execution', following the real path of a rule from configuration to going live.
1. Overall Architecture
The overall architecture diagram focuses on "how rules flow." A rule starts from page configuration, goes through approval, release, configuration center distribution, and client caching, and finally enters the business service's local runtime for execution. The core design trade-off is: centralized management and publishing, localized execution.
2. Model Relationships
The model relationship diagram focuses on "how rules are expressed." The management console saves edit-state models; during release, they are assembled into
Chain JSON and written to the configuration center; after the business service pulls them, they are deserialized into runtime Chains and Components, then handed over to the executor for processing.
3. Full Rule Publishing Process
Rule publishing is the key bridge from the management plane to the runtime plane: ChainDTO → Chain JSON → RPC write to configuration server → Apollo push to each business Pod.
3.1 Chain Status
3.2 Publishing Logic
4. Release Approval Process
5. Dual-Run Comparison and Grayscale Traffic Switching
The platform stipulates {chainKey}Compare as the shadow chain Key, verifying new rules without affecting the main chain's results.
5.1 Management Console Publishing Logic
5.2 Runtime Execution Branch
Process and Code Mapping (Runtime)
| Process Step | Key Entry Point |
|---|---|
| execute chainKey | ChainExecutor.execute |
| Load chainKeyCompare | ChainHelper.getByChainCode(appendCompareSuffix(key)) |
| Different versions and flowPercent is empty → Dual Run | needCompare → doExecute(main) + compare(...) |
| Different versions and hit ratio → Traffic Switch | needFlow + randomPercent <= flowPercent |
| Same version → Normal Execution | doExecute(main) |
| JsonDiff records differences | compare() inside JsonDiff.asJson(old, new) |
Key Code (Rule Engine SDK · ChainExecutor)
// executeAndComparedAndFlow — corresponds to the three branches in the flowchart
Chain chainCompare = ChainHelper.getByChainCode(appendCompareSuffix(chain.getKey()));
if (needCompare(chain, chainCompare)) { // Different versions && flowPercent == null
ChainExecuteResult result = doExecute(chain, params);
compare(chain, result, chainCompare, params); // Shadow execution + JsonDiff, result still based on main chain
return result;
}
if (needFlow(chain, chainCompare) && randomPercent <= chainCompare.getFlowPercent()) {
return doExecute(chainCompare, params); // Traffic switch to new chain
}
return doExecute(chain, params); // Normal execution
// needCompare / needFlow judgment conditions
boolean needCompare = !main.version.equals(compare.version) && compare.flowPercent == null;
boolean needFlow = !main.version.equals(compare.version) && compare.flowPercent != null;
| Phase | Status | Compare Chain flowPercent | Runtime Behavior |
|---|---|---|---|
| Comparison | COMPARE | null | Main chain returns; shadow executes Compare and JsonDiff |
| Traffic Switch | FLOW | 1~100 | Routes to Compare chain based on ratio |
| Full Rollout | PUBLISHED | null (same version) | No more comparison/traffic switching |
6. Business-Side Rule Configuration Pulling
After each business service introduces the configuration consumption SDK, configuration consistency is maintained through three paths.
Process and Code Mapping
| Process Step | Module | Key Entry Point |
|---|---|---|
| Register chainKey + Compare | Rule Engine SDK | LiteflowPropertyAutoConfiguration.parseKeys |
| Service Startup → RPC Pull | Config Consumption SDK | TradeConfigStarter.start → TradeConfigRepository.sync(STARTER) |
| batchFetchConfig RPC | Config Consumption SDK | TradeConfigRepository.sync → TradeConfigClient.batchFetchConfig |
| releaseVersion fallback | Config Server | TradeConfigService.getTradeConfig |
| Apollo Push → Parse Change | Config Consumption SDK | ApolloListener.listen → needUpdate → updateConfig |
| Scheduled Fallback Sync | Config Consumption SDK | TradeConfigSyncSchedule → sync(SCHEDULE) |
| Write to Local Cache | Config Consumption SDK | fireConfigChange → TradeConfig.putValue |
| Change Notification → Hot Reload | Rule Engine SDK | TradeConfigNotify → ApolloParseHelper.listen → ChainHelper.reload |
Key Code
// [Startup] TradeConfigStarter — corresponds to the 'Service Startup' trigger source
@PostConstruct
public void start() {
tradeConfigRepository.sync(SyncSource.STARTER); // RPC pull
apolloListener.listen(); // Apollo listener
tradeConfigSyncSchedule.start(); // Scheduled fallback
}
// [Startup/Scheduled] TradeConfigRepository.sync — corresponds to 'batchFetchConfig RPC'
Lists.partition(keys, 10).forEach(batch -> {
condition.setReleaseVersion(ReleaseVersionUtil.getReleaseVersion());
tradeConfigFacade.batchFetchConfig(condition); // → Config Server RPC
});
fireConfigChange(key, oldValue, newValue, syncSource); // → Write cache + notify
// [Apollo] ApolloListener — corresponds to the 'Apollo Push' branch
config.addChangeListener(event -> {
if (!needUpdate(tradeConfigKey, apolloKey)) return; // Grayscale version routing
tradeConfigRepository.updateConfig(key, apolloValue);
});
// [Registration] Rule Engine Starter — registers Compare chain simultaneously for each chainKey
keys.add(chainKey);
keys.add(appendCompareSuffix(chainKey));
// [fallback] Config Server — falls back to default when grayscale version not found
if (tradeConfig == null && !DEFAULT.equals(releaseVersion)) {
return getTradeConfigWithReleaseVersion(list, key, DEFAULT);
}
7. Rule Compilation and Hot Loading
Process and Code Mapping
| Process Step | Module | Key Entry Point |
|---|---|---|
| Read Chain JSON | Rule Engine SDK | TradeConfig.getProperty(MODULE, chainKey) |
| parseChain + checkChain | Rule Engine SDK | ChainHelper.parseChain |
| Register Script Node | Rule Engine SDK | ChainHelper.loadChainScript → LiteFlowNodeBuilder |
| Register Chain EL | Rule Engine SDK | ChainHelper.loadChain → LiteFlowChainELBuilder |
| Config UPDATE → Hot Load | Rule Engine SDK | ApolloParseHelper.listen → ChainHelper.reload |
| Config DELETE → Unload | Rule Engine SDK | ChainHelper.unload (reference count reaches zero) |
Key Code (Rule Engine SDK)
// [Startup] ApolloParseHelper.initFromApollo — corresponds to 'Read → Parse → Register'
for (RuleEngineKey key : keys) {
String chainJson = TradeConfig.getProperty(MODULE, key.getChainKey());
Chain chain = ChainHelper.parseChain(chainJson); // parseChain + checkChain
ChainHelper.load(chain);
}
// [load] ChainHelper — corresponds to 'Register Script Node + Chain EL'
public static void load(Chain chain) {
loadChainScript(chain, chain); // LiteFlowNodeBuilder.createScriptNode().build()
loadChain(chain, chain); // LiteFlowChainELBuilder.createChain().setEL(...).build()
}
// [Hot Update] ApolloParseHelper.listen — corresponds to the two fork paths
TradeConfig.addListener(event -> {
switch (changeType) {
case ADD, UPDATE -> ChainHelper.reload(newChain); // unload old chain → load new chain
case DELETE -> ChainHelper.unload(chain); // Unload after reference count reaches zero
}
});
// [reload] ChainHelper.reload
if (oldChain != null) unload(oldChain);
load(chain);
8. Rule Execution Principle
Process and Code Mapping
| Process Step | Module | Key Entry Point |
|---|---|---|
| ChainExecutor.execute | Rule Engine SDK | ChainExecutor.execute(chainId, params) |
| Compare / Traffic Switch / Normal Routing | Rule Engine SDK | executeAndComparedAndFlow |
| Memory version vs Cache version fallback | Rule Engine SDK | tryReloadChain |
| Acquire chain lock | Rule Engine SDK | chain.getLock().tryLock() |
| Construct context | Rule Engine SDK | new ChainContext() + new ChainResultContext() |
| LiteFlow Execution | Rule Engine SDK | flowExecutor.execute2Resp(chain.getCode(), params, contexts) |
| Encapsulate result | Rule Engine SDK | chainResultContext.getData() → ChainExecuteResult |
Key Code (Rule Engine SDK · ChainExecutor)
// execute — Entry point, first routes then executes
public ChainExecuteResult execute(String chainId, Map<String, Object> params) {
Chain chain = ChainHelper.getByChainCode(chainId);
return executeAndComparedAndFlow(chain, params); // Compare/Traffic Switch/Normal routing
}
// doExecute — corresponds to the flowchart 'Version fallback → Lock → Construct context → LiteFlow execution'
private ChainExecuteResult doExecute(Chain chain, Map<String, Object> params) {
chain = tryReloadChain(chain); // Memory vs Cache version fallback
chain.getLock().tryLock(2, TimeUnit.SECONDS);
ChainContext ctx = new ChainContext();
ChainResultContext resultCtx = new ChainResultContext();
params = ChainHelper.handleParamsDataType(chain, params);
flowExecutor.execute2Resp(chain.getCode(), params, ctx, resultCtx);
return new ChainExecuteResult(resultCtx.getData());
}
// tryReloadChain — Auto reload when versions are inconsistent
Chain chainReload = ChainHelper.parseChain(TradeConfig.getProperty(MODULE, chain.getKey()));
if (!chain.getVersion().equals(chainReload.getVersion())) {
ChainHelper.reload(chainReload);
return chainReload;
}
End-to-End Main Link
Page Configuration → Test Verification → Approval → compare → flow → publish
→ Config Server persistence + Apollo push
→ Config SDK pull/listen → Local cache
→ Rule SDK compilation and loading → LiteFlow registration
→ ChainExecutor executes business requests
9. From Configuration Model to Execution Model: How LiteFlow Takes Over Rules
The platform itself does not directly implement a process execution engine, but converts "visually configured rules" into an execution model recognizable by LiteFlow. This allows focusing efforts on rule configuration, release governance, hot updates, and business abstraction, while leaving underlying process orchestration, node scheduling, and context passing to a mature process framework.
The core concepts in LiteFlow are:
| LiteFlow Concept | Meaning in the Platform |
|---|---|
| Chain | A rule chain, corresponding to a "chain" in the platform |
| EL | Chain orchestration expression, describing the sequence, conditions, branches, and combination relationships of components |
| Node | An executable node, corresponding to a "component" in the platform |
| FlowBus | LiteFlow's runtime registry, saving loaded Chains and Nodes |
| FlowExecutor | LiteFlow's execution entry point, executing the corresponding chain based on chainId |
| Context | Execution context, used to pass input parameters, intermediate results, and final results among multiple components |
9.1 From Visual Chain to LiteFlow Chain
The chain configured on the management console is ultimately published as a Chain JSON. This JSON contains the chain code, EL expression, component list, sub-chains, parameter types, version number, enabled status, grayscale ratio, and other information.
A simplified structure can be understood as:
{
"key": "address_edit_strategy",
"el": "THEN(checkCity, checkOrderStatus, buildResult)",
"enable": true,
"version": 12,
"flowPercent": 10,
"cmpList": [
{
"code": "checkCity",
"componentClass": "SCRIPT",
"script": "cityId in supportCityList"
},
{
"code": "checkOrderStatus",
"componentClass": "SCRIPT",
"script": "orderStatus == 'WAIT_ACCEPT'"
},
{
"code": "buildResult",
"componentClass": "SCRIPT",
"script": "setData(chainResultCtx, 'allowEditAddress', true)"
}
]
}
After the runtime SDK starts, it reads this Chain JSON from the configuration cache and does two things:
- Registers the
elin the chain as a LiteFlow Chain. - Registers each component in the chain as a LiteFlow Node.
In other words, what the business side sees is "a rule chain configured," while what LiteFlow sees at the bottom layer is "an executable Chain + multiple executable Nodes."
9.2 EL Expressions Describe Orchestration Relationships
EL is LiteFlow's orchestration language. The "chain canvas" in the platform is ultimately converted into EL expressions and handed over to LiteFlow for parsing.
For example, sequential execution can be expressed as:
THEN(checkCity, checkOrderStatus, buildResult)
With conditional branches, it can be expressed as:
IF(checkRisk, denyResult, THEN(checkCity, buildResult))
This layer of conversion is critical. The visualization platform exposes flowcharts, components, and fields to users; what the runtime actually executes is LiteFlow's EL. This improves usability on the configuration side while maintaining stability on the execution side.
9.3 Components Are Converted into LiteFlow Nodes
Components in the platform have two typical implementation methods:
- Java Components: Written in advance and registered in the Spring container, suitable for stable and complex logic.
- Script Components: Delivered with script content during release, dynamically registered as script nodes at runtime, suitable for frequently changing judgment logic.
For script components, the runtime registers the component code, script content, and script language into LiteFlow's Node. When a component executes, it can retrieve input parameters from the context and write results back to the context.
For example, a script component can do something like this:
Read cityId, orderStatus
-> Determine if conditions are met
-> Write the judgment result to chainCtx or chainResultCtx
In this process, LiteFlow is responsible for "when to execute this node," while the platform is responsible for "what specific business logic this node executes."
9.4 FlowBus Is the Runtime Rule Registry
LiteFlow's runtime registers Chains and Nodes into FlowBus. FlowBus can be understood as an in-memory rule execution registry:
chainMapstores chain definitions.nodeMapstores node definitions.- During execution, the corresponding Chain is found based on chainId.
- The Chain then finds the Nodes to execute based on EL.
The platform's hot update capability is essentially built around FlowBus. When the configuration center pushes new rules, the runtime parses the new Chain JSON and executes:
Unload old Chain / old Script Node
-> Register new Chain / new Script Node
-> Subsequent requests automatically use the new version rules
To prevent concurrent modification of chains during execution, the runtime typically implements necessary lock control at the chain dimension and uses version numbers to determine if the local cache needs reloading.
9.5 FlowExecutor Is Responsible for Actually Executing Chains
Business services ultimately do not directly operate FlowBus, but call the execution entry point encapsulated by the platform. The execution entry point first performs some platform-level logic:
- Finds the main chain based on chainId.
- Determines if a comparison chain exists.
- Determines if dual-run comparison is needed.
- Determines if the grayscale ratio is hit.
- Constructs the rule execution context.
After preparation is complete, it calls LiteFlow's FlowExecutor to execute:
FlowExecutor.execute2Resp(chainCode, params, contextBeans)
LiteFlow finds the corresponding Chain based on chainCode, parses the EL, and executes Nodes in sequence or branches. Each Node shares the same set of contexts during execution, so the calculation results of a previous component can be used by subsequent components.
9.6 Context Strings Together the Entire Execution Process
Rule execution is not simply returning true or false. Many business scenarios require reading original input parameters, saving intermediate judgment results, and outputting structured results. Therefore, the platform encapsulates two core contexts on top of LiteFlow's context mechanism:
- ChainContext: Stores the current chain, request input parameters, and intermediate calculation results. Components can read variables from here or look up results from other components.
- ChainResultContext: Stores the final output result. Result components write the return values needed by the business here.
Taking the previous address editing rule as an example:
checkCity
-> Reads cityId from ChainContext
-> Writes citySupportEdit = true
checkOrderStatus
-> Reads orderStatus from ChainContext
-> Writes orderCanEditAddress = true
buildResult
-> Reads the results of the previous two components
-> Writes allowEditAddress, reason to ChainResultContext
Finally, ChainExecutor encapsulates the data in ChainResultContext into a unified execution result and returns it to the business service.
9.7 Why Not Directly Call the Rule Center Remotely for Execution
This design choice of "the rule center is responsible for configuration and publishing, and the business service's local SDK is responsible for execution" is mainly based on several considerations:
- More Stable Performance: Rule execution occurs within the business service process, eliminating the need for remote calls to the rule center for every request.
- Better Availability: When the configuration center is temporarily unavailable, the business service can still continue execution using rules in the local cache.
- Sufficiently Fast Hot Updates: Configuration changes enter the local cache through listening and scheduled synchronization, then trigger LiteFlow reload.
- Easier Business Extension: Business services can pass in their own context Beans and register local Java components.
So LiteFlow plays the role of a "local process execution kernel" here, while the rule visualization configuration platform plays the role of "rule asset management, configuration publishing, and runtime adaptation." Sections 6~8 above have expanded on configuration pulling, compilation hot loading, and execution details from the code level; what is supplemented here is the conceptual mapping at the LiteFlow layer.
At this point, the main link from "how configuration is published" to "how rules are executed" is complete. However, to understand why this platform can carry complex business rules, it is necessary to return to several basic abstractions on the configuration side: Spaces, Categories, Options, Components, Chains. They determine whether the management console is easy to use and whether rule assets can be continuously accumulated.
7. Several Key Abstractions
If only looking at model names, concepts like Spaces, Categories, Options, Components, and Chains can seem quite abstract. A more intuitive way to understand them is to place them in a specific business scenario.
Suppose there is a rule: when a user modifies an order address, the system needs to determine whether the modification is allowed. This judgment might be affected by multiple factors, such as city, order status, user type, vehicle type, whether the order has been accepted, whether a risk strategy has been hit, etc. The final rule might output:
{
"allowEditAddress": true,
"reason": "Current city and order status allow address modification"
}
If written in code, such rules often become a series of if/else statements. If written as ordinary configuration, they easily evolve into a difficult-to-maintain JSON. What the platform does is break down this business judgment into several clearly layered, reusable abstractions.
Space: First Solve the "Who Manages Whom" Problem
A space can be understood as the business boundary of rule assets. For example, address editing strategies, invoicing strategies, and unbinding strategies can be placed in different spaces. Each space has its own fields, components, chains, and permissions, independent of each other.
This prevents the rule platform from becoming a public configuration area without boundaries. Personnel related to address editing only manage the address editing space, and personnel related to invoicing only manage the invoicing space. Once permission boundaries are clear, rule configuration is suitable for opening up to more business parties to participate.
Category: Making Rule Assets Easier to Find
When a space has more and more fields and components, relying solely on name search becomes difficult to maintain. Categories are used to group rule assets.
Still using the address editing rule example above, fields can be divided into "Order Fields," "City Fields," "User Fields," "Risk Control Fields," and components into "Status Judgment," "City Judgment," "Result Output." Later, when configuration personnel want to find a field like "whether the order has been accepted by a driver," they don't need to search among all fields but can directly look under the Order Field category.
Categories themselves do not participate in rule execution, but they determine whether the platform is easy to use. The more rule assets there are, the more important classification becomes.
Options: Turning Business Fields into Rule-Recognizable Variables
Options are fields or variables that can be used within rules. They can come from business input parameters or be intermediate results after component execution.
For example, the address editing rule might maintain these options:
| Option | Meaning | Type |
|---|---|---|
cityId |
City ID | Number |
orderStatus |
Order Status | Enum |
userType |
User Type | Enum |
isDriverAccepted |
Whether driver has accepted order | Boolean |
allowEditAddress |
Whether address editing is allowed | Boolean |
With options, rule configuration is no longer "handwriting field names." Configuration personnel can select fields from the page, and the platform can also perform validation based on field types, reducing low-level errors caused by mixing numbers, strings, and booleans.
Components: Depositing a Piece of Judgment Logic as a Reusable Unit
A component can be understood as a method within a rule. It receives some options as input and produces a result after judgment.
For example, several components can be broken out:
- Determine if city supports address editing: Input
cityId, outputcitySupportEdit. - Determine if order status allows address modification: Input
orderStatus,isDriverAccepted, outputorderCanEditAddress. - Generate final result: Input the previous two judgment results, output
allowEditAddress,reason.
Components can have different types:
- Boolean Component: Answers "yes or no" questions, such as "does the order allow address modification."
- Selection Component: Answers "which one to choose" questions, such as "which processing method should be adopted for the current scenario."
- Result Component: Responsible for organizing the final output, such as returning whether allowed and the reason.
Components can also have different implementation methods. Stable and complex logic is suitable for writing as Java components, such as logic that needs to access local services or reuse complex algorithms; frequently adjusted logic with simpler expressions is suitable for script components, such as conditional judgments between certain fields.
Chains: Orchestrating Components into a Complete Business Decision
A chain is the complete rule process. It organizes multiple components in sequence, conditions, and branches to form a complete business decision.
The chain for the address editing rule can be abstracted as:
Start
-> Determine if city supports address editing
-> Determine if order status allows address modification
-> If both judgments pass, output allowEditAddress = true
-> Otherwise output allowEditAddress = false and reason
End
At runtime, the business service only needs to call:
execute("address_edit_strategy", requestParams)
The SDK will execute the corresponding components according to the chain definition, put intermediate results into the context, and finally return a structured result.
After these abstractions are combined, rule configuration is no longer "configuring a piece of JSON," but forms a maintainable rule asset system: Spaces solve isolation and permissions, Categories solve organization and search, Options solve field standardization, Components solve logic reuse, and Chains solve complete decision orchestration.
When these abstractions and the underlying runtime mechanisms are all in place, the change brought by the platform is not just "more convenient configuration," but the entire lifecycle of rules—from development, verification, launch, to rollback—is reorganized.
8. Practical Effects
The rule visualization configuration platform is suitable for undertaking these types of scenarios:
- Business switches with many judgment conditions that change frequently.
- Business strategies that need to output structured results.
- Rules that need to branch by dimensions such as city, channel, business line, user group, and order status.
- High-risk configurations requiring release approval, online comparison, grayscale traffic switching, and quick rollback.
- Complex business domains hoping to accumulate field, component, and chain assets.
Efficiency Improvement
| Before Transformation | After Transformation |
|---|---|
| Free-form configuration structure, prone to manual maintenance errors | Page-based visual editing, structured management of fields, components, and chains |
| Difficult regression scenarios, requiring construction of real business links | Page test cases can directly construct parameters and execute rules |
| Weak awareness of rule changes by responsible persons after release | Unified control through approval, notification, and release cards |
| Rules scattered in code and configuration, difficult to reuse | Fields, components, and chains accumulated as rule assets |
| High risk of one-time full rollout for changes | Dual-run comparison, grayscale traffic switching, quick rollback |
These effects are not brought about by a single function, but are the result of the combined action of structured expression, release governance, local execution, and hot update mechanisms.
9. Summary
The value of the rule visualization configuration platform is not just "moving rules to a page for configuration," but abstracting complex business rules from scattered code and configuration to form a complete set of expression, verification, release, execution, and governance mechanisms.
The key design behind it can be summarized in three points:
- Structuring complex business rules using fields, components, and chains.
- Safely distributing rules and executing them locally in business services using the configuration center and runtime SDK.
- Controlling rule change risks using test cases, approval, dual-run, grayscale, and snapshot rollback.
When business rules become more numerous and change more frequently, relying solely on code branches or ordinary configuration centers will gradually become difficult to maintain. Platformizing, assetizing, and governing rules is the direction for long-term maintainability.