DEV Community

Alexandros Korovesis
Alexandros Korovesis

Posted on

Stop Hardcoding Your Workflows: Meet the Rule-Driven JSON Flow Engine for Java

Managing complex business processes in Java often leads to "Service Layer Bloat." You know the feeling: one service method that calls five others, with complex branching logic and error handling that's impossible to visualize.

I decided to build a lightweight library to solve this: a JSON-based engine that treats your business logic as a dynamic map rather than a static script.

The Problem: Rigid Workflows vs. "Heavy" Alternatives
Usually, if you want to change the order of two operations or add a conditional check, you have to modify the Java code, recompile, and redeploy.

Why not just use BPMN?
Many enterprise developers turn to BPMN (like Camunda or Flowable) for these problems. While powerful, BPMN comes with significant baggage:

  • Heavyweight Libraries: They often require complex database schemas, massive dependencies, and a lot of boilerplate to set up.

  • Not Truly Human-Readable: While the diagrams look nice, the underlying XML is a nightmare to read, version control, or manually edit.

  • Steep Learning Curve: You often need specialized training just to configure the engine.

I wanted something lightweight, JSON-native, and Spring-friendly.

The Solution.
Camino allows you to define a "Flow" as a series of Blocks in a JSON file.

1. Define the Flow
In Camino, you define a sequence of START, ACTION, INTERSECTION, and END blocks. Here's a snippet where we check a user's name before deciding which action to take (condition expressions use MVEL):

{
    "name": "myFirstFlow",
    "blocks": [
        {
            "id": "1",
            "type": "START",
            "nextId": "2"
        },
        {
            "id": "2",
            "name": "getNameFromIdAction",
            "type": "ACTION",
            "action": "getNameFromIdActionHandler",
            "nextId": "3"
        },
        {
            "id": "3",
            "name": "checkNameIntersection",
            "type": "INTERSECTION",
            "conditions": [
                {
                    "name": "yes",
                    "expression": "name.equals('George')",
                    "nextId": "4"
                },
                {
                    "name": "no",
                    "defaultCondition": true,
                    "nextId": "5"
                }
            ]
        },
        {
            "id": "4",
            "name": "updateUserAction",
            "type": "ACTION",
            "action": "updateUserActionHandler",
            "nextId": "6"
        },
        {
            "id": "5",
            "name": "informUserAction",
            "type": "ACTION",
            "action": "informUserActionHandler",
            "nextId": "6"
        },
        {
            "id": "6",
            "type": "END"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

2. Implement the Logic
You don't lose the power of Java. You start the execution of the flow as defined in your JSON file by calling the FlowExecutor method execute and passing as parameters the name of the flow and a map to carry all your data (process context). The same map will be used to evaluate each condition in your flow with the provided key. For every ACTION in your JSON, you simply create a class that extends AbstractActionHandler for each action in your JSON file and declare the bean using the same name from the corresponding "action" property.
You process context is propagated in each action in the flow as a HashMap and can me accessed from the ctx property of the execute method.
Finally use this map to construct the response data.

public class GetNameFromIdActionHandler extends AbstractActionHandler {

    @Override
    public void execute(HashMap<String, Object> ctx) {
        System.out.println("executing get name from ID action handler");
    }
}
Enter fullscreen mode Exit fullscreen mode
    @Bean
    public GetNameFromIdActionHandler getNameFromIdActionHandler() {
        return new GetNameFromIdActionHandler();
    }
Enter fullscreen mode Exit fullscreen mode

3. Enable the library
Add spring.camino.enabled=true to your application properties file.


Final Thoughts

Hardcoding business logic into your Java services creates a rigid architecture that's expensive to change. By adopting a flow-based approach, you give your application the flexibility to grow alongside your business requirements.

⭐️ Check it out on GitHub: https://github.com/a-koro/camino
🚀 See the demo: https://github.com/a-koro/caminoDemoApplication

Top comments (0)