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.

IT Services Circle
IT Services Circle
IT Services Circle
Eliminate if…else Hell with the Lightweight Easy Rules Engine

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.0
Note: 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/

Easy Rules architecture diagram
Easy Rules architecture diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendJavarule enginesoftware designEasy Rules
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.