diff --git a/rule-engines-modules/pom.xml b/rule-engines-modules/pom.xml
index dcd97c5f275a..bc163f8034a6 100644
--- a/rule-engines-modules/pom.xml
+++ b/rule-engines-modules/pom.xml
@@ -18,6 +18,7 @@
evrete
openl-tablets
rulebook
+ simple-rule-engine
diff --git a/rule-engines-modules/simple-rule-engine/pom.xml b/rule-engines-modules/simple-rule-engine/pom.xml
new file mode 100644
index 000000000000..4911464dfe7a
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+ com.baeldung.simpleruleengine
+ simple-rule-engine
+ 1.0
+ simple-rule-engine
+
+
+ com.baeldung
+ rule-engines-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ org.springframework
+ spring-expression
+ ${spring-expression.version}
+
+
+
+
+ 7.0.0-M7
+
+
+
\ No newline at end of file
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java
new file mode 100644
index 000000000000..514cc74ad4cd
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/FirstOrderHighValueSpecialDiscountRule.java
@@ -0,0 +1,17 @@
+package com.baeldung.ruleengine;
+
+import com.baeldung.ruleengine.model.Order;
+
+public class FirstOrderHighValueSpecialDiscountRule implements IRule {
+
+ @Override
+ public boolean evaluate(Order order) {
+ return order.getCustomer()
+ .isFirstOrder() && order.getAmount() > 500;
+ }
+
+ @Override
+ public String description() {
+ return "First Order Special Discount Rule: First Time customer with high value order";
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java
new file mode 100644
index 000000000000..1f79f49fdd4b
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/IRule.java
@@ -0,0 +1,8 @@
+package com.baeldung.ruleengine;
+
+import com.baeldung.ruleengine.model.Order;
+
+public interface IRule {
+ boolean evaluate(Order order);
+ String description();
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java
new file mode 100644
index 000000000000..86bd39fab28a
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/LoyaltyDiscountRule.java
@@ -0,0 +1,16 @@
+package com.baeldung.ruleengine;
+
+import com.baeldung.ruleengine.model.Order;
+
+public class LoyaltyDiscountRule implements IRule{
+
+ @Override
+ public boolean evaluate(Order order) {
+ return order.getCustomer().getLoyaltyPoints() > 500;
+ }
+
+ @Override
+ public String description() {
+ return "Loyalty Discount Rule: Customer has more than 500 points";
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java
new file mode 100644
index 000000000000..cc199cfa85da
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/RuleEngine.java
@@ -0,0 +1,21 @@
+package com.baeldung.ruleengine;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.baeldung.ruleengine.model.Order;
+
+public class RuleEngine {
+ private final List rules;
+
+ public RuleEngine(List rules) {
+ this.rules = rules;
+ }
+
+ public List evaluate(Order order) {
+ return rules.stream()
+ .filter(rule -> rule.evaluate(order))
+ .map(IRule::description)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java
new file mode 100644
index 000000000000..9a386bf9f2f4
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/SpelRule.java
@@ -0,0 +1,31 @@
+package com.baeldung.ruleengine;
+
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import com.baeldung.ruleengine.model.Order;
+
+
+public class SpelRule {
+
+ private final String expression;
+ private final String description;
+
+ public SpelRule(String expression, String description) {
+ this.expression = expression;
+ this.description = description;
+ }
+
+ public boolean evaluate(Order order) {
+ ExpressionParser parser = new SpelExpressionParser();
+ StandardEvaluationContext context = new StandardEvaluationContext(order);
+ context.setVariable("order", order);
+ return parser.parseExpression(expression)
+ .getValue(context, Boolean.class);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java
new file mode 100644
index 000000000000..8264a6afaf4e
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Customer.java
@@ -0,0 +1,26 @@
+package com.baeldung.ruleengine.model;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+
+@Setter
+@Getter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Customer {
+
+ String name;
+ int loyaltyPoints;
+ boolean firstOrder;
+
+ public Customer() {
+ }
+
+ public Customer(String name, int loyaltyPoints, boolean firstOrder) {
+ this.name = name;
+ this.loyaltyPoints = loyaltyPoints;
+ this.firstOrder = firstOrder;
+ }
+
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java
new file mode 100644
index 000000000000..4f9d0c7979f7
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/java/com/baeldung/ruleengine/model/Order.java
@@ -0,0 +1,25 @@
+package com.baeldung.ruleengine.model;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+
+@Getter
+@Setter
+public class Order {
+
+ private Double amount;
+ private Customer customer;
+
+ public Order() {
+ }
+
+ public Order(Double amount, Customer customer) {
+ this.amount = amount;
+ this.customer = customer;
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml b/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml
new file mode 100644
index 000000000000..7d900d8ea884
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java
new file mode 100644
index 000000000000..c6f792e01776
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/RuleEngineUnitTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.ruleengine;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+import com.baeldung.ruleengine.model.Customer;
+import com.baeldung.ruleengine.model.Order;
+
+public class RuleEngineUnitTest {
+
+ @Test
+ void whenTwoRulesTriggered_thenBothDescriptionsReturned() {
+ Customer customer = new Customer("Max", 550, true);
+ Order order = new Order(600.0, customer);
+
+ RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule()));
+
+ List results = engine.evaluate(order);
+
+ assertEquals(2, results.size());
+ assertTrue(results.contains("Loyalty Discount Rule: Customer has more than 500 points"));
+ assertTrue(results.contains("First Order Special Discount Rule: First Time customer with high value order"));
+ }
+
+ @Test
+ void whenNoRulesTriggered_thenEmptyListReturned() {
+ Customer customer = new Customer("Max", 50, false);
+ Order order = new Order(200.0, customer);
+
+ RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule()));
+
+ List results = engine.evaluate(order);
+
+ assertTrue(results.isEmpty());
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java
new file mode 100644
index 000000000000..3d4a2c131027
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/test/java/com/baeldung/ruleengine/SpelRuleUnitTest.java
@@ -0,0 +1,34 @@
+package com.baeldung.ruleengine;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.baeldung.ruleengine.model.Customer;
+import com.baeldung.ruleengine.model.Order;
+
+public class SpelRuleUnitTest {
+
+ @Test
+ void whenLoyalCustomer_thenEligibleForDiscount() {
+ Customer customer = new Customer("Bob", 730, false);
+ Order order = new Order(200.0, customer);
+
+ SpelRule rule = new SpelRule(
+ "#order.customer.loyaltyPoints > 500",
+ "Loyalty discount rule"
+ );
+ assertTrue(rule.evaluate(order));
+ }
+
+ @Test
+ void whenFirstOrderHighAmount_thenEligibleForSpecialDiscount() {
+ Customer customer = new Customer("Bob", 0, true);
+ Order order = new Order(800.0, customer);
+
+ SpelRule approvalRule = new SpelRule(
+ "#order.customer.firstOrder and #order.amount > 500",
+ "First-time customer with high order gets special discount"
+ );
+ assertTrue(approvalRule.evaluate(order));
+ }
+}
diff --git a/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml b/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml
new file mode 100644
index 000000000000..7d900d8ea884
--- /dev/null
+++ b/rule-engines-modules/simple-rule-engine/src/test/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file