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.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Replace if…else with a Lightweight Rule Engine: Introducing Easy Rules

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 match

Maven 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/

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.

JavaRule EngineBackend DevelopmentSpring BootMVELEasy RulesComposite Rules
Su San Talks Tech
Written by

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.

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.