Replace if…else with a Lightweight Rule Engine: Introducing Easy Rules
The article explains why deep nesting of if…else statements harms readability, maintainability and testability, and shows how the Java Easy Rules engine can extract business logic into reusable rule objects with multiple definition styles, lightweight architecture, and integration options.
Problem
Code reviews revealed business methods with 7‑8 nested if…else blocks, >300 lines, causing poor readability, maintainability, testability, extensibility, tight coupling, duplicated logic and required service restarts.
Moving rules to a database does not solve composition, priority, condition evaluation and automatic execution.
Easy Rules Overview
Easy Rules is a lightweight Java rule engine (≈100 KB) that provides a Rule abstraction and a RulesEngine API. It moves conditional logic out of code into independent rule objects.
Core Concepts
Rule
A rule has name , description , priority , a condition returning true and an action executed when the condition holds.
public interface Rule {
boolean evaluate(Facts facts);
void execute(Facts facts) throws Exception;
// getters for name, description, priority …
}Facts
Key‑value container passed to rules.
Facts facts = new Facts();
facts.put("user", user);
facts.put("order", order);
facts.put("rain", true);Rules (rule set)
Ordered collection sorted by priority.
Rules rules = new Rules();
rules.register(new VipDiscountRule());
rules.register(new NewUserDiscountRule());
rules.register(new DoubleElevenRule());RulesEngine
Two implementations:
DefaultRulesEngine : executes rules in priority order, optionally stopping after the first applied rule.
InferenceRulesEngine : forward‑chaining inference; repeatedly fires all currently true rules until no rule can fire.
Definition Methods
Annotation (most common)
@Rule(name = "VIP Discount Rule", priority = 1)
public class VipDiscountRule {
@Condition
public boolean isVip(@Fact("user") User user) { return user.isVip(); }
@Action
public void applyDiscount(@Fact("order") Order order) { order.setDiscount(0.9); }
}Fluent API
Rule dynamicRule = new RuleBuilder()
.name("High‑Temp Alert")
.description("Alert when temperature > 30°C")
.priority(2)
.when(facts -> facts.get("temperature") > 30)
.then(facts -> System.out.println("Heat warning!"))
.build();Expression Language (MVEL example)
Rule mvelRule = new MVELRule()
.name("Member Discount")
.description("10% off for members with order > 100")
.when("user.vip == true && order.amount > 100")
.then("order.discount = 0.9");YAML/JSON configuration
# rules.yml
- name: "New User First Order Discount"
description: "8% off for first order"
priority: 3
condition: "user.newUser == true && user.orderCount == 0"
actions:
- "order.discount = 0.8"Engine Execution Flow
DefaultRulesEngine processes the rule set sequentially by priority, evaluating each rule and executing the action when the condition is true.
InferenceRulesEngine uses the following loop:
Enter inference loop.
Find all rules whose condition evaluates to true under the current Facts (candidate rules).
If no candidates, exit.
Execute all candidate rules (they may modify Facts).
Repeat from step 2.
Listener Mechanism
Custom RuleListener implementations can hook into beforeEvaluate , afterEvaluate , onSuccess and onFailure events.
public class LoggingRuleListener implements RuleListener {
@Override
public boolean beforeEvaluate(Rule rule, Facts facts) {
System.out.println("Evaluating rule: " + rule.getName());
return true;
}
@Override
public void afterEvaluate(Rule rule, Facts facts, boolean result) {
System.out.println("Rule " + rule.getName() + " result: " + result);
}
@Override
public void onSuccess(Rule rule, Facts facts) {
System.out.println("Rule " + rule.getName() + " succeeded");
}
@Override
public void onFailure(Rule rule, Facts facts, Exception e) {
System.err.println("Rule " + rule.getName() + " failed: " + e.getMessage());
}
}Composite Rules
Three group types:
UnitRuleGroup (AND): all contained rules must fire.
ActivationRuleGroup (XOR): the first satisfied rule (highest priority) fires.
ConditionalRuleGroup : the first rule decides whether subsequent rules are evaluated.
UnitRuleGroup unitGroup = new UnitRuleGroup("All‑Match Group");
unitGroup.addRule(new VipRule());
unitGroup.addRule(new AmountRule());
unitGroup.addRule(new StockRule()); // fires only if all three matchMaven Dependency (textual)
Add the following dependencies (version 4.1.0) to your pom.xml:
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>4.1.0</version>
</dependency>Note: Easy Rules entered maintenance mode in December 2020; the latest stable version is 4.1.x.
Pros
Lightweight (~100 KB JAR, millisecond startup).
POJO‑based with annotations.
Four definition styles cover code‑centric to configuration‑driven needs.
Supports composite rules (AND, XOR, conditional).
Listener API for logging, metrics and debugging.
Zero‑configuration out‑of‑the‑box.
Seamless Spring Boot integration.
Cons
Rule changes still require recompilation/restart unless using dynamic class loading or expression‑language configs.
Lacks advanced features such as decision tables and complex rule chains found in Drools.
Project is in maintenance mode; no new major features.
Performance may degrade with thousands of rules compared to Rete‑based engines.
No built‑in visual rule management UI.
Suitable Scenarios
E‑commerce promotions, fraud‑risk checks, data validation, conditional flows (registration, login), dynamic recommendation, game AI behavior.
Configuration‑driven rule management via YAML/JSON.
Less Suitable Scenarios
Very large rule sets (thousands of rules) where performance is critical.
Projects requiring a visual rule authoring UI.
Use‑cases demanding hot‑swap rule updates without restart.
Complex decision‑table or rule‑flow requirements.
Resources
GitHub: https://github.com/j-easy/easy-rules
Official documentation: https://www.easyrules.org/
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
