VOOZH about

URL: https://dzone.com/articles/runtime-formula-evaluation-mvel

⇱ Runtime Formula Evaluation With MVEL Library in Spring Boot


Related

  1. DZone
  2. Coding
  3. Frameworks
  4. Runtime Formula Evaluation With MVEL Library in Spring Boot

Runtime Formula Evaluation With MVEL Library in Spring Boot

Learn how to use MVEL in Spring Boot to move business rules into the database, reduce deployments, and support dynamic runtime calculations.

By Jun. 16, 26 · Analysis
Likes
Comment
Save
1.5K Views

Join the DZone community and get the full member experience.

Join For Free

In our software development processes, business units constantly want to update discount rates, loyalty points, or salary calculation logic.

If this logic is within the code, between when-or-if-else blocks, every change means a new unit test process, code analysis, CI/CD pipeline work, and ultimately a "deployment."

In this article, we will separate the business logic from the code, making it manageable in the database and reliably interpretable at runtime. By increasing flexibility, we will ensure the system's stable operation continues without interruption.

To do all this, we will examine how to use the MVEL (MVFLEX Expression Language) library below.

The Cost of Static Code: Why Should We Avoid It?

Generally, point calculations are as follows:

Kotlin
fun calculatePoints(pointType: String, factor: Int): Long {
 return when (pointType) {
 "INITIAL" -> 100L
 "BIRTHDAY" -> 50L
 "TENURE_5_10" -> factor * 10L
 "TENURE_10_20" -> factor * 20L
 "TENURE_20_PLUS" -> factor * 30L
 else -> 0L
 }
}


When looking at the code, what appears is more of a maintenance burden than a simple function. If the factors change or a new rule is added, the code is triggered from the beginning.

However, these values are actually data, not code.

Architectural Approach

Below, you will find how it works when we add the Formula engine.

Kotlin
import org.mvel2.MVEL

val formula = "factor * 20"
val vars = mapOf("factor" to 5)
val result = MVEL.eval(formula, vars)


In this architecture, the code does not know "how to calculate"; It only knows how to call the 'Formula engine.'

Database Design

Converting Rules to Data

We can store business rules in a flexible table. This ensures manageability.

PLSQL
CREATE TABLE t_point_type (
 point_type_id NUMBER PRIMARY KEY,
 point_type_name VARCHAR2(100),
 point_formula VARCHAR2(500), 
 description VARCHAR2(1000)
);


Sample data:

Plain Text
| point_type_id | point_type_name | point_formula |
|:-------------:|:---------------:|:-----------------------:|
| 1 | INITIAL | `100` |
| 2 | BIRTHDAY | `50` |
| 3 | TENURE_5_10 | `factor * 10` |
| 4 | TENURE_10_20 | `factor * 20` |
| 5 | TENURE_20_PLUS | `factor * 30` |
| 6             | PROMOTIONAL     | `factor * multiplier`   |


Application Layer

The most critical point to consider in MVEL integration is performance and error management.

1. Entity Definition

Kotlin
@Entity
@Table(name = "t_point_type")
data class PointTypeEntity(
 @Id
 @Column(name = "point_type_id")
 val pointTypeId: Long? = null,

 @Column(name = "point_type_name")
 val pointTypeName: String? = null,

 @Column(name = "point_formula")
 val pointFormula: String? = null
)


2. MvelUtil: Performance-Oriented Helper Class

Considering the CPU cost of parsing strings in every request, we should use compiled expressions and caching mechanisms.

Kotlin
@Component
class MvelUtil {

 fun evaluateFormula(formula: String, factor: Int): Long {
 return try {
 val variables = mapOf("factor" to factor)
 val result = MVEL.eval(formula, variables)
 when (result) {
 is Number -> result.toLong()
 else -> 0L
 }
 } catch (e: Exception) {
 throw BusinessException(
 errorCode = ErrorCodes.MVEL_FORMULA_EVALUATION_FAILED,
 errorDesc = "Formula evaluation failed: $formula, factor: $factor — ${e.message}"
 )
 }
 }

 fun evaluateFormulaAsString(formula: String, factor: Int): String {
 return try {
 val variables = mapOf("factor" to factor)
 MVEL.eval(formula, variables).toString()
 } catch (e: Exception) {
 throw BusinessException(
 errorCode = ErrorCodes.MVEL_FORMULA_EVALUATION_FAILED,
 errorDesc = "Formula evaluation failed.: $formula — ${e.message}"
 )
 }
 }
}


3. Service Layer and Business Logic

Therefore, our service layer simply receives the data and triggers the formula engine.

Kotlin
@Service
class PointCalculationService(
 private val pointTypeRepository: PointTypeRepository,
 private val mvelUtil: MvelUtil
) {

 fun calculatePoints(pointTypeId: Long, factor: Int): Long {
 val pointType = pointTypeRepository.findById(pointTypeId)
 .orElseThrow { BusinessException(ErrorCodes.POINT_TYPE_NOT_FOUND) }

 val formula = pointType.pointFormula
 ?: throw BusinessException(ErrorCodes.POINT_FORMULA_NOT_DEFINED)

 val points = mvelUtil.evaluateFormula(formula, factor)

 if (points <= 0) {
 log.info("The formula gave a score of 0 or negative: type=$pointTypeId, factor=$factor")
 return 0L
 }

 return points
 }
}


Call service:

Kotlin
val factor = inputData.factorSpecificForPoint ?: 1
val points = calculatePoints(inputData.pointTypeId, factor)

if (points > 0) {
 savePointDetail(points, subscriptionId, inputData.pointTypeId, inputData.operationId)
}


Advanced Usage: Multivariable and Conditional Formulas

MVEL has the ability to decode complex strings. Its true power lies in this.

For example, the formula in the database might look like this:

SQL
UPDATE t_point_type 
SET point_formula = 'factor * multiplier + bonus'
WHERE point_type_id = 6;
Kotlin
fun evaluateWithMultipleVars(formula: String, vars: Map<String, Any>): Long {
 return try {
 val result = MVEL.eval(formula, vars)
 (result as? Number)?.toLong() ?: 0L
 } catch (e: Exception) {
 throw BusinessException(ErrorCodes.MVEL_FORMULA_EVALUATION_FAILED)
 }
}

val vars = mapOf("factor" to 5, "multiplier" to 3, "bonus" to 10)
evaluateWithMultipleVars("factor * multiplier + bonus", vars)  


Conditional Statements

MVEL supports ternary expressions and Boolean logic:

Plain Text
factor > 10 ? factor * 20 : factor * 10
(factor >= 5 && factor < 10) ? 50 : (factor >= 10 ? 100 : 25)


This provides truly dynamic rules without any code changes.

We must not ignore these three rules, as everything is necessary;

  • Strict validation: The formula must be validated with MVEL.compileExpression() before being saved to the database. An incorrect syntax error can disrupt the entire flow at runtime.
  • Sandbox and security: MVEL is robust; it can access Java classes. Therefore, formula entry should only be done from authorized (admin) panels, and if necessary, MVEL's secure mode should be configured.
  • Default value: There can always be a fallback mechanism. We determine how the system will behave if the formula receives an error or the result returns null (e.g., 0 points).

Conclusion

MVEL makes it easy for us to dynamically implement business rules in Spring Boot projects.

It reduces code complexity while allowing you to respond to business unit requests within minutes (without deployment!).

XML
Dependency (Maven):

XML
<dependency>
<groupId>org.mvel</groupId> 
<artifactId>mvel2</artifactId> 
<version>2.5.0.Final</version>
</dependency>


Evaluation Library Spring Boot

Opinions expressed by DZone contributors are their own.

Related

  • Configurable Feign Client Retry With Reusable Library and DRY
  • That’s How You Can Use MapStruct With Lombok in Your Spring Boot Application
  • Parallel Kafka Batch Processing With Kotlin Coroutines in Spring Boot
  • A Spring Boot App With Half the Startup Time

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

Let's be friends: