• Uncategorised

Why should we use langgraph instead of traditional programming

# LangGraph4j Integration Specification

## Table of Contents

1. [Why LangGraph4j?](#why-langgraph)

2. [Core Concepts](#core-concepts)

3. [Java Implementation Example](#java-example)

4. [ColdFusion Integration Proposal](#coldfusion-integration)

## 1. Why LangGraph4j? {#why-langgraph}

### The Problem: Traditional Workflow Limitations

In traditional application development, complex workflows are often implemented using:

❌ **Nested if-else statements** – Hard to maintain, test, and visualize

❌ **Hard-coded logic** – Difficult to modify without code changes

❌ **Procedural code** – No clear separation of concerns

❌ **Fragile dependencies** – Changes in one part break other parts

**Example of problematic code:**

“`java

public void processPaymentRequest(PaymentRequest request) {

// Validation

if (request.getAmount() <= 0) {

throw new ValidationException(“Invalid amount”);

}

// Approval logic

if (request.getAmount() < 1000) {

// Auto-approve

request.setStatus(“approved”);

processPayment(request);

} else if (request.getAmount() < 10000) {

// Manager approval

if (managerApproved(request)) {

request.setStatus(“approved”);

processPayment(request);

} else {

request.setStatus(“rejected”);

}

} else {

// CEO approval

if (ceoApproved(request)) {

request.setStatus(“approved”);

processPayment(request);

} else {

request.setStatus(“rejected”);

}

}

// Notification

sendNotification(request);

// Audit logging

logAudit(request);

}

“`

**Problems with this approach:**

– πŸ”΄ Logic is tightly coupled

– πŸ”΄ Hard to add new approval levels

– πŸ”΄ Cannot visualize the workflow

– πŸ”΄ Difficult to test individual steps

– πŸ”΄ Cannot reuse approval logic elsewhere

– πŸ”΄ No way to track workflow state

### The Solution: Graph-Based Workflows

**LangGraph4j** treats workflows as **directed graphs** where:

βœ… **Nodes** = Individual processing steps (validate, approve, notify)

βœ… **Edges** = Flow between steps (what happens next)

βœ… **State** = Data that flows through the workflow

βœ… **Conditional Edges** = Dynamic routing based on conditions

**Benefits:**

| Aspect | Traditional Code | LangGraph4j |

|——–|—————–|————-|

| **Clarity** | Nested if-else | Clear graph structure |

| **Modularity** | Tightly coupled | Independent nodes |

| **Testability** | Hard to isolate | Test nodes individually |

| **Flexibility** | Hard-coded logic | Dynamic routing |

| **Visualization** | None | Visual graph representation |

| **Reusability** | Difficult | Nodes can be reused |

### Real-World Use Case: Payment Approval System

**Business Requirements:**

1. **Validate** payment request (amount, account, etc.)

2. **Route** to appropriate approver based on amount:

– < $1,000: Auto-approve

– $1,000 – $10,000: Manager approval

– > $10,000: Finance Director approval

3. **Process** the approved payment

4. **Notify** relevant parties

5. **Log** for audit trail

**With LangGraph4j, this becomes:**

“`

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ START β”‚

β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜

β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ Validate β”‚

β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ Route β”‚ (Conditional)

β””β”€β”€β”¬β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”˜

β”‚ β”‚ β”‚

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ └──────────┐

β”‚ β”‚ β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”

β”‚Auto-approveβ”‚ β”‚ Manager β”‚ β”‚ Director β”‚

β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜

β”‚ β”‚ β”‚

β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ Process β”‚

β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ Notify β”‚

β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”‚

β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚ END β”‚

β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

“`

Each step is **independent**, **testable**, and **reusable**!

## 2. Core Concepts {#core-concepts}

### 2.1 State

**State** is a Map-based data structure that flows through the workflow. Think of it as a “data packet” that each node reads from and writes to.

“`java

// State is just a HashMap that flows through nodes

Map<String, Object> state = new HashMap<>();

state.put(“amount”, 5000);

state.put(“requester”, “John Doe”);

state.put(“account”, “ACC-12345”);

“`

### 2.2 Nodes

**Nodes** are individual processing steps. Each node:

– **Reads** current state

– **Performs** its logic

– **Returns** updates to add to state

“`java

private static Map<String, Object> validateRequest(AgentState state) {

// 1. Read from state

int amount = (int) state.data().get(“amount”);

// 2. Perform validation logic

boolean valid = amount > 0 && amount <= 100000;

// 3. Return updates

Map<String, Object> updates = new HashMap<>();

updates.put(“valid”, valid);

updates.put(“validation_message”, valid ? “OK” : “Invalid amount”);

return updates;

}

“`

### 2.3 Edges

**Edges** define what happens next. Two types:

**Direct Edge**: Always goes from A β†’ B

“`java

workflow.addEdge(“validate”, “process”); // Always: validate β†’ process

“`

**Conditional Edge**: Routes based on state

“`java

workflow.addConditionalEdges(

“check_amount”, // From this node

state -> routeByAmount(state), // Use this function to decide

Map.of( // Possible destinations

“auto”, “auto_approve”,

“manual”, “manager_review”

)

);

“`

## 3. Java Implementation Example {#java-example}

Let’s implement the payment approval workflow shown in the diagram above.

“`java

package ai;

import org.bsc.langgraph4j.*;

import org.bsc.langgraph4j.state.AgentState;

import java.util.*;

import java.util.concurrent.CompletableFuture;

import static org.bsc.langgraph4j.StateGraph.END;

import static org.bsc.langgraph4j.StateGraph.START;

public class PaymentApprovalWorkflow {

// ================================================================

// STEP 1: Define Nodes

// ================================================================

/**

* Node 1: Validate the payment request

*/

private static Map<String, Object> validateRequest(AgentState state) {

System.out.println(“\n[VALIDATE] Validating payment request…”);

// Read from state

int amount = (int) state.data().getOrDefault(“amount”, 0);

String account = (String) state.data().getOrDefault(“account”, “”);

// Validation logic

boolean valid = amount > 0 && amount <= 100000 && !account.isEmpty();

System.out.println(” Amount: $” + amount);

System.out.println(” Account: ” + account);

System.out.println(” Valid: ” + valid);

// Return updates

Map<String, Object> updates = new HashMap<>();

updates.put(“valid”, valid);

updates.put(“validation_message”, valid ? “OK” : “Invalid request”);

return updates;

}

/**

* Node 2: Auto-approve for small amounts

*/

private static Map<String, Object> autoApprove(AgentState state) {

System.out.println(“\n[AUTO-APPROVE] Small amount, automatically approved”);

Map<String, Object> updates = new HashMap<>();

updates.put(“approved”, true);

updates.put(“approver”, “System”);

updates.put(“approval_type”, “automatic”);

return updates;

}

/**

* Node 3: Manager review for medium amounts

*/

private static Map<String, Object> managerReview(AgentState state) {

System.out.println(“\n[MANAGER] Reviewing payment request”);

int amount = (int) state.data().get(“amount”);

System.out.println(” Amount: $” + amount);

System.out.println(” Decision: Approved by manager”);

Map<String, Object> updates = new HashMap<>();

updates.put(“approved”, true);

updates.put(“approver”, “Manager”);

updates.put(“approval_type”, “manager”);

return updates;

}

/**

* Node 4: Director review for large amounts

*/

private static Map<String, Object> directorReview(AgentState state) {

System.out.println(“\n[DIRECTOR] Reviewing high-value payment request”);

int amount = (int) state.data().get(“amount”);

System.out.println(” Amount: $” + amount);

System.out.println(” Decision: Approved by finance director”);

Map<String, Object> updates = new HashMap<>();

updates.put(“approved”, true);

updates.put(“approver”, “Finance Director”);

updates.put(“approval_type”, “director”);

return updates;

}

/**

* Node 5: Process the approved payment

*/

private static Map<String, Object> processPayment(AgentState state) {

System.out.println(“\n[PROCESS] Processing approved payment”);

String approver = (String) state.data().get(“approver”);

System.out.println(” Approved by: ” + approver);

System.out.println(” Status: Payment processed”);

Map<String, Object> updates = new HashMap<>();

updates.put(“status”, “completed”);

updates.put(“transaction_id”, “TXN-” + System.currentTimeMillis());

return updates;

}

/**

* Node 6: Send notification

*/

private static Map<String, Object> sendNotification(AgentState state) {

System.out.println(“\n[NOTIFY] Sending notification”);

String status = (String) state.data().get(“status”);

String requester = (String) state.data().get(“requester”);

System.out.println(” To: ” + requester);

System.out.println(” Message: Payment ” + status);

Map<String, Object> updates = new HashMap<>();

updates.put(“notified”, true);

return updates;

}

// ================================================================

// STEP 2: Define Routing Logic

// ================================================================

/**

* Routing function: Decides which approval path based on amount

*/

private static String routeByAmount(AgentState state) {

int amount = (int) state.data().getOrDefault(“amount”, 0);

if (amount < 1000) {

System.out.println(“\n[ROUTE] Amount < $1,000 β†’ Auto-approve”);

return “auto”;

} else if (amount < 10000) {

System.out.println(“\n[ROUTE] Amount < $10,000 β†’ Manager review”);

return “manager”;

} else {

System.out.println(“\n[ROUTE] Amount β‰₯ $10,000 β†’ Director review”);

return “director”;

}

}

// ================================================================

// STEP 3: Build the Workflow Graph

// ================================================================

public static CompiledGraph<AgentState> buildWorkflow() throws Exception {

// Create new workflow

StateGraph<AgentState> workflow = new StateGraph<>(AgentState::new);

// Add all nodes to the graph

workflow.addNode(“validate”,

state -> CompletableFuture.completedFuture(validateRequest(state)));

workflow.addNode(“auto_approve”,

state -> CompletableFuture.completedFuture(autoApprove(state)));

workflow.addNode(“manager_review”,

state -> CompletableFuture.completedFuture(managerReview(state)));

workflow.addNode(“director_review”,

state -> CompletableFuture.completedFuture(directorReview(state)));

workflow.addNode(“process”,

state -> CompletableFuture.completedFuture(processPayment(state)));

workflow.addNode(“notify”,

state -> CompletableFuture.completedFuture(sendNotification(state)));

// Define edges (workflow flow)

// START β†’ validate (always start with validation)

workflow.addEdge(START, “validate”);

// validate β†’ CONDITIONAL ROUTING based on amount

workflow.addConditionalEdges(

“validate”, // From node

state -> CompletableFuture.completedFuture(routeByAmount(state)), // Routing function

Map.of( // Path mapping

“auto”, “auto_approve”,

“manager”, “manager_review”,

“director”, “director_review”

)

);

// All approval paths lead to process

workflow.addEdge(“auto_approve”, “process”);

workflow.addEdge(“manager_review”, “process”);

workflow.addEdge(“director_review”, “process”);

// process β†’ notify β†’ END

workflow.addEdge(“process”, “notify”);

workflow.addEdge(“notify”, END);

// Compile and return

return workflow.compile();

}

// ================================================================

// STEP 4: Execute the Workflow

// ================================================================

public static void main(String[] args) {

try {

System.out.println(“╔════════════════════════════════════════════════════════════╗”);

System.out.println(“β•‘ Payment Approval Workflow Demo β•‘”);

System.out.println(“β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•”);

CompiledGraph<AgentState> workflow = buildWorkflow();

// ============================================================

// TEST 1: Small amount (< $1,000) β†’ Auto-approve

// ============================================================

System.out.println(“\n” + “=”.repeat(60));

System.out.println(“TEST 1: Small Payment ($500)”);

System.out.println(“=”.repeat(60));

Map<String, Object> smallPayment = new HashMap<>();

smallPayment.put(“amount”, 500);

smallPayment.put(“account”, “ACC-12345”);

smallPayment.put(“requester”, “John Doe”);

workflow.stream(smallPayment).forEach(output -> {

// Just consume the stream

});

// ============================================================

// TEST 2: Medium amount ($1,000-$10,000) β†’ Manager review

// ============================================================

System.out.println(“\n\n” + “=”.repeat(60));

System.out.println(“TEST 2: Medium Payment ($5,000)”);

System.out.println(“=”.repeat(60));

Map<String, Object> mediumPayment = new HashMap<>();

mediumPayment.put(“amount”, 5000);

mediumPayment.put(“account”, “ACC-67890”);

mediumPayment.put(“requester”, “Jane Smith”);

workflow.stream(mediumPayment).forEach(output -> {

// Just consume the stream

});

// ============================================================

// TEST 3: Large amount (β‰₯ $10,000) β†’ Director review

// ============================================================

System.out.println(“\n\n” + “=”.repeat(60));

System.out.println(“TEST 3: Large Payment ($50,000)”);

System.out.println(“=”.repeat(60));

Map<String, Object> largePayment = new HashMap<>();

largePayment.put(“amount”, 50000);

largePayment.put(“account”, “ACC-11111”);

largePayment.put(“requester”, “Bob Johnson”);

workflow.stream(largePayment).forEach(output -> {

// Just consume the stream

});

System.out.println(“\n\n” + “=”.repeat(60));

System.out.println(“βœ“ All tests completed successfully!”);

System.out.println(“=”.repeat(60));

} catch (Exception e) {

e.printStackTrace();

}

}

}

“`

### Expected Output

“`

╔════════════════════════════════════════════════════════════╗

β•‘ Payment Approval Workflow Demo β•‘

β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

============================================================

TEST 1: Small Payment ($500)

============================================================

[VALIDATE] Validating payment request…

Amount: $500

Account: ACC-12345

Valid: true

[ROUTE] Amount < $1,000 β†’ Auto-approve

[AUTO-APPROVE] Small amount, automatically approved

[PROCESS] Processing approved payment

Approved by: System

Status: Payment processed

[NOTIFY] Sending notification

To: John Doe

Message: Payment completed

============================================================

TEST 2: Medium Payment ($5,000)

============================================================

[VALIDATE] Validating payment request…

Amount: $5000

Account: ACC-67890

Valid: true

[ROUTE] Amount < $10,000 β†’ Manager review

[MANAGER] Reviewing payment request

Amount: $5000

Decision: Approved by manager

[PROCESS] Processing approved payment

Approved by: Manager

Status: Payment processed

[NOTIFY] Sending notification

To: Jane Smith

Message: Payment completed

============================================================

TEST 3: Large Payment ($50,000)

============================================================

[VALIDATE] Validating payment request…

Amount: $50000

Account: ACC-11111

Valid: true

[ROUTE] Amount β‰₯ $10,000 β†’ Director review

[DIRECTOR] Reviewing high-value payment request

Amount: $50000

Decision: Approved by finance director

[PROCESS] Processing approved payment

Approved by: Finance Director

Status: Payment processed

[NOTIFY] Sending notification

To: Bob Johnson

Message: Payment completed

============================================================

βœ“ All tests completed successfully!

============================================================

“`

### Key Takeaways

1. **Nodes Are Functions**: Each node is a simple function that:

– Takes `AgentState` as input

– Returns `Map<String, Object>` with updates

– Is wrapped in `CompletableFuture.completedFuture()` for async execution

2. **Edges Define Flow**:

– **Direct edges**: `workflow.addEdge(“process”, “notify”)` – always goes from process β†’ notify

– **Conditional edges**: `workflow.addConditionalEdges(…)` – routes based on function result

3. **State Flows Through Workflow**:

– Initial state: `{amount: 500, account: “ACC-12345”, requester: “John”}`

– After validate: `{…, valid: true, validation_message: “OK”}`

– After approve: `{…, approved: true, approver: “System”}`

– After process: `{…, status: “completed”, transaction_id: “TXN-…”}`

– After notify: `{…, notified: true}`

4. **Conditional Routing Is Dynamic**: The same workflow executes different paths based on runtime data (amount), without any if-else statements in the main flow!

5. **Each Node Is Independent**: You can test `validateRequest()` separately, reuse it in other workflows, or replace it without affecting other nodes.

## 4. ColdFusion Integration Proposal {#coldfusion-integration}

*[To be continued in Part 2…]*

## Summary

**LangGraph4j** provides a clean, modular way to build complex workflows by treating them as graphs:

– **Nodes**: Independent processing steps

– **Edges**: Define the flow (direct or conditional)

– **State**: Data that flows through the workflow

– **Dynamic Routing**: Conditional edges enable runtime decisions

This approach replaces tangled if-else logic with a clear, visual, testable workflow structure.

You may also like...