Replace if…else with a lightweight rule engine – Introducing Easy Rules

The article explains why extensive if…else chains degrade code readability, maintainability, and testability, and introduces Easy Rules—a lightweight Java rule engine that externalizes business logic into reusable rule objects, offering annotation, fluent API, expression, and YAML/JSON definitions, plus composite rules and engine configurations.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Replace if…else with a lightweight rule engine – Introducing Easy Rules

Problem with nested if‑else

A typical business method can contain seven or eight nested if...else statements, inflating the method to over 300 lines. Adding, modifying, or deleting a rule then requires code changes, recompilation and redeployment, which leads to poor readability, maintainability, testability, extensibility and tight coupling between business logic and code.

Easy Rules overview

Easy Rules is an open‑source Java rule engine (maintained by the j‑easy team) that implements Martin Fowler’s rule‑engine concept: a collection of objects, each with a condition and an action, evaluated at runtime.

Core abstractions

Rule : defines name, description, priority, a condition method returning boolean, and an action method.

Facts : a key‑value container passed to rules; rule methods obtain data via @Fact parameters.

Rules : an ordered collection of Rule objects automatically sorted by priority.

RulesEngine : executes the rules. Two implementations are provided:

DefaultRulesEngine

Executes rules in priority order; when a rule’s condition evaluates to true, its action runs. Suitable for most scenarios.

InferenceRulesEngine

Implements forward‑chaining inference: repeatedly finds all rules whose conditions are true, executes them (which may modify Facts), and loops until no new rule can fire. Ideal when rules depend on each other.

Engine parameters

RulesEngineParameters parameters = new RulesEngineParameters()
    .skipOnFirstAppliedRule(true)   // stop after first successful rule
    .skipOnFirstFailedRule(true)
    .skipOnFirstNonTriggeredRule(false)
    .priorityThreshold(10);          // only execute rules with priority ≤10
RulesEngine engine = new DefaultRulesEngine(parameters);

Listener mechanism

Custom RuleListener implementations can hook into lifecycle events (before/after evaluation, success, failure) for logging, monitoring or debugging.

public class LoggingRuleListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        System.out.println("Evaluating rule: " + rule.getName());
        return true; // return false to skip
    }
    @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() + " executed successfully");
    }
    @Override
    public void onFailure(Rule rule, Facts facts, Exception e) {
        System.err.println("Rule " + rule.getName() + " failed: " + e.getMessage());
    }
}

Four ways to define rules

1. Annotation (most common)

@Rule(name = "Weather rule", description = "Take an umbrella when 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‑temperature 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 (MVEL example)

Rule mvelRule = new MVELRule()
    .name("Member discount")
    .description("VIP and order > 100 gets 10% off")
    .when("user.vip == true && order.amount > 100")
    .then("order.discount = 0.9");

4. YAML/JSON configuration

# rules.yml
- name: "New‑user first order discount"
  description: "First order for new users gets 20% off"
  priority: 3
  condition: "user.newUser == true && user.orderCount == 0"
  actions:
    - "order.discount = 0.8"

Load with a rule factory:

MVELRuleFactory factory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule rule = factory.createRule(new FileReader("rules.yml"));

Composite rules

Easy Rules can combine simple rules into complex logic using three group types:

UnitRuleGroup (AND) : all contained rules must fire before the group executes.

ActivationRuleGroup (XOR‑like) : the first matching rule (by priority) is executed, others are ignored.

ConditionalRuleGroup : execution proceeds only while the first rule’s condition is true; subsequent rules run conditionally.

// UnitRuleGroup example
UnitRuleGroup allMatch = new UnitRuleGroup("All‑match group");
allMatch.addRule(new VipRule());
allMatch.addRule(new AmountRule());
allMatch.addRule(new StockRule());

Architecture layers

The engine consists of four layers: FactsRuleRulesRulesEngine. The DefaultRulesEngine processes rules sequentially by priority; the InferenceRulesEngine repeatedly evaluates all true conditions until no new rule can fire.

Maven coordinates

Add the following dependencies (version 4.1.0 is the latest stable release):

<!-- Easy Rules core -->
org.jeasy:easy-rules-core:4.1.0

<!-- YAML/JSON support -->
org.jeasy:easy-rules-support:4.1.0

<!-- MVEL expression support -->
org.jeasy:easy-rules-mvel:4.1.0

Note : Easy Rules entered maintenance mode in Dec 2020; only the 4.1.x line receives updates.

Advantages

Extremely lightweight (~100 KB JAR); startup time drops to milliseconds, suitable for containerised environments.

Low learning curve: POJO + annotations, no DSL required.

Multiple rule definition styles cover the spectrum from hard‑coded to fully externalised.

Supports composite rules for complex business logic.

Listener mechanism enables logging, performance monitoring and debugging.

Zero‑configuration out‑of‑the‑box; can be integrated into Spring Boot with a simple @Bean registration.

Drawbacks

Rule changes still require code modification and service restart unless combined with dynamic class loading or expression‑based rules.

Does not support advanced decision tables or Rete‑based optimisations; less suitable for very large rule sets.

Performance may degrade with thousands of rules compared to Drools.

No built‑in visual rule management UI.

Typical use cases

E‑commerce promotions: discounts, coupons, VIP privileges.

Risk control: user scoring, fraud detection.

Data validation: form checks, integrity verification.

Conditional branching: registration, login flows.

Dynamic decision: personalised recommendations.

Game AI: NPC behaviour rules.

Configuration‑driven management: YAML/JSON rule files.

When not to use

Very large rule bases (thousands of rules) – performance inferior to Rete‑based engines.

Need for visual rule authoring – Easy Rules provides no UI.

Hot‑update of rules at runtime – requires extra dynamic loading mechanisms.

Complex decision tables – engine capabilities are limited.

Resources

GitHub repository: 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 EngineAnnotationMVELYAMLEasy RulesFluent API
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.