Eliminate if…else Hell with the Lightweight Easy Rules Engine
The article examines the drawbacks of deep if…else chains in Java business logic and introduces Easy Rules, a lightweight, annotation‑based rule engine that separates rules from code, improves readability, maintainability, testability, and supports multiple definition styles.
Problem with nested if...else
Long methods with many if...else branches become hard to read, maintain, test, and extend. Adding, removing or changing a rule requires locating the correct branch, recompiling and redeploying, and often a service restart. The same condition may appear in multiple places, coupling business logic to code.
Moving the logic to a database does not solve composition, priority, condition evaluation, and automatic execution; a real rule engine is required.
Easy Rules Overview
Easy Rules is a lightweight Java rule engine (≈100 KB JAR) that extracts business rules from code and manages them as objects. It follows Martin Fowler’s rule‑engine concept: create objects with condition and action, store them in a collection, and fire them.
Core Features
POJO‑based development with annotations.
Multiple definition methods: annotations, fluent API, MVEL/SpEL/JEXL expression languages, and YAML/JSON configuration.
Composite rules (AND, XOR, conditional).
Easy integration with Spring Boot and other frameworks.
Core Concepts
Rule
The Rule interface defines four elements:
name : unique identifier.
description : brief text.
priority : lower numbers mean higher priority.
condition : returns true to trigger the rule.
action : executed when the condition is satisfied.
public interface Rule {
boolean evaluate(Facts facts); // evaluate condition
void execute(Facts facts) throws Exception; // perform action
// getters for name, description, priority, etc.
}Facts
A key‑value container that holds the data context for rule execution.
Facts facts = new Facts();
facts.put("user", user);
facts.put("order", order);
facts.put("rain", true);Rules (rule set)
An ordered container that stores multiple Rule objects and sorts them by priority.
Rules rules = new Rules();
rules.register(new VipDiscountRule());
rules.register(new NewUserDiscountRule());
rules.register(new DoubleElevenRule());RulesEngine
The engine executes the rule set. Two implementations are provided:
DefaultRulesEngine : executes rules in priority order; fires a rule when its condition is true. Suitable for most regular scenarios.
InferenceRulesEngine : forward‑chaining inference; repeatedly fires rules until no more can be triggered. Suitable when rules depend on each other.
// Default engine
RulesEngine engine = new DefaultRulesEngine();
engine.fire(rules, facts);Engine Parameter Configuration
RulesEngineParameters parameters = new RulesEngineParameters()
.skipOnFirstAppliedRule(true) // stop after first successful rule
.skipOnFirstFailedRule(true) // stop after first failure
.skipOnFirstNonTriggeredRule(false)
.priorityThreshold(10); // only execute rules with priority ≤ 10
RulesEngine engine = new DefaultRulesEngine(parameters);Listener Mechanism
Custom listeners can hook into the rule lifecycle for logging, monitoring, or debugging.
public class LoggingRuleListener implements RuleListener {
@Override
public boolean beforeEvaluate(Rule rule, Facts facts) {
System.out.println("Start evaluating rule: " + rule.getName());
return true; // return false to skip rule
}
@Override
public void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
System.out.println("Rule " + rule.getName() + " evaluation result: " + evaluationResult);
}
@Override
public void onSuccess(Rule rule, Facts facts) {
System.out.println("Rule " + rule.getName() + " executed successfully");
}
@Override
public void onFailure(Rule rule, Facts facts, Exception exception) {
System.err.println("Rule " + rule.getName() + " failed: " + exception.getMessage());
}
}Composite Rules
Easy Rules provides three composite rule types to combine simple rules.
UnitRuleGroup (AND logic)
UnitRuleGroup unitGroup = new UnitRuleGroup("All‑must‑match group");
unitGroup.addRule(new VipRule());
unitGroup.addRule(new AmountRule());
unitGroup.addRule(new StockRule());
// Fires only when all contained rules are satisfied.ActivationRuleGroup (XOR logic)
ActivationRuleGroup activationGroup = new ActivationRuleGroup("Discount group");
activationGroup.addRule(new NewUserDiscountRule()); // priority=3
activationGroup.addRule(new VipDiscountRule()); // priority=1
// Only the highest‑priority matching rule is executed.ConditionalRuleGroup (conditional chain)
ConditionalRuleGroup conditionalGroup = new ConditionalRuleGroup("Conditional group");
conditionalGroup.addRule(new CheckUserTypeRule()); // decides user type
conditionalGroup.addRule(new VipDiscountRule());
conditionalGroup.addRule(new NormalDiscountRule());
// Subsequent rules are evaluated only if the first rule matches.Four Ways to Define Rules
1. Annotation (most common)
@Rule(name = "Weather Rule", description = "Take an umbrella if it rains", priority = 1)
public class WeatherRule {
@Condition
public boolean isRaining(@Fact("rain") boolean rain) {
return rain;
}
@Action
public void takeUmbrella() {
System.out.println("It's raining, remember to take an umbrella!");
}
}2. Fluent API (dynamic rules)
Rule dynamicRule = new RuleBuilder()
.name("High‑Temp Alert")
.description("Warn when temperature exceeds 30°C")
.priority(2)
.when(facts -> facts.get("temperature") > 30)
.then(facts -> System.out.println("Hot weather, stay cool!"))
.build();3. Expression Language (most flexible)
Rule mvelRule = new MVELRule()
.name("Member Discount")
.description("9% discount for members with order > 100")
.when("user.vip == true && order.amount > 100")
.then("order.discount = 0.9");4. YAML/JSON Configuration (full externalisation)
# rules.yml
- name: "New User First Order Discount"
description: "8% off for first order of a new user"
priority: 3
condition: "user.newUser == true && user.orderCount == 0"
actions:
- "order.discount = 0.8" MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule rule = ruleFactory.createRule(new FileReader("rules.yml"));Maven Coordinates
Add the following dependencies to your pom.xml (version 4.1.0):
org.jeasy:easy-rules-core:4.1.0
org.jeasy:easy-rules-support:4.1.0
org.jeasy:easy-rules-mvel:4.1.0Note: Easy Rules entered maintenance mode in December 2020; the latest stable version is 4.1.x. Users are encouraged to upgrade.
Pros and Cons
Advantages
Extremely lightweight (~100 KB JAR); startup time in milliseconds.
Low learning curve: POJO‑based model and annotations.
Multiple rule definition styles cover the spectrum from hard‑coded to fully externalised.
Composite rule support (AND, XOR, conditional).
Listener mechanism for logging, performance monitoring, and debugging.
Zero‑configuration out‑of‑the‑box usage.
Seamless Spring Boot integration via @Bean.
Disadvantages
Rule changes still need code modification and service restart unless combined with dynamic class loading or expression‑language approaches.
Lacks advanced features such as complex rule chains and decision tables found in heavyweight engines like Drools.
Project is in maintenance mode; no new features beyond version 4.1.x.
Performance may degrade for rule sets with thousands of rules compared to Rete‑based engines.
No built‑in visual rule‑management UI; developers must build their own.
Suitable Scenarios
E‑commerce promotion rules (discounts, coupons, member privileges).
Fraud detection (user scoring, anomaly detection, anti‑fraud).
Data validation (form checks, data integrity verification).
Conditional branching (user registration, login flows).
Dynamic decision making (personalized recommendations, adaptive services).
Game AI (NPC behavior, decision logic).
Configuration‑driven management (YAML/JSON rule files).
Scenarios Not Well‑Suited
Very large rule sets (thousands of rules) – performance inferior to Drools‑style engines.
Need for visual rule management UI – Easy Rules provides none.
Real‑time hot rule updates – requires custom dynamic loading or expression‑language tricks.
Complex decision tables – engine capabilities are limited.
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.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
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.
