Groovy, an LLM, and Three Chained Orchestrators: How I Solved the Hardest JDE Array Comparison I’ve Ever Faced

The brief that sounded simple

The client explained it in two sentences: compare what the sale looks like in JDE with what an external system wants to do with it. Specifically, how that external system wanted to distribute the sale across different lines.

On paper, it sounds like basic conditional logic.

The reality had three layers that complicated each other:

  • Validate whether what the external system was requesting was even feasible given the current state of the order in JDE.
  • Generate a first array with the changes to apply on existing lines.
  • Generate a second array with the brand new lines that needed to be added.

Two output arrays with completely different natures. Business rules that shifted with every project iteration. And production waiting.

Architecture diagram of three chained Orchestrators in JD Edwards for sales array comparison and modification

First attempt: LEX

The natural first move was LEX. It’s the standard tool for conditional logic inside an Orchestrator without stepping outside the native environment. For fixed rules and predictable criteria, it works well.

It worked until it didn’t.

Business conditions started evolving. Exceptions stacking on exceptions. Field combinations nobody had anticipated in the original design. In Lex, every change was surgery with real risk of breaking what was already validated. And the pace of changes in the project left no room for that kind of fragility.

At some point you have to accept that Lex is not the right tool for this problem.

Moving to Groovy and the first real friction

Groovy in the Manipulate Input step is the answer when the logic outgrows what Lex can express. You get the full language: loops, arbitrary data structures, unlimited conditional logic.

But there are two concrete friction points that complicate the work inside a Custom Request.

The first is input. Receiving data from a Data Request or Form Request has its own structural rules, and if you don’t handle them precisely, you end up debugging something that’s not a logic error — it’s a payload formatting problem.

The second, and more limiting for this case, is output. A Custom Request in Orchestrator returns one array. One. And here the process needed to produce two datasets with completely different structure and purpose.

With a single Orchestrator, there’s no clean solution.

The architecture that actually worked

The solution arrived when I stopped trying to solve everything in one place and split the problem by responsibility.

The main Orchestrator acts as the conductor. It receives the information from the external system along with the JDE sale data and has one clear responsibility: coordinate the full flow without executing business logic itself.

Its first action is to call the second Orchestrator — the comparison one — passing both initial datasets. This second Orchestrator is where all the intelligence lives. In the Manipulate Input step with Groovy, it applies the business rules: validates whether the requested distribution is viable given the actual state of the order, calculates the deltas between what exists and what’s being requested, and classifies each difference. If during that process it detects inconsistencies or error conditions, it captures them and returns them structured. If validation passes, it returns the two resulting arrays: the one with changes to apply on existing lines and the one with new lines to add.

Control returns to the main Orchestrator. This is where error validation happens. If the second Orchestrator returned errors, the flow stops and the external system is notified with the exact detail of the problem. If there are no errors, the main Orchestrator calls the third one, passing it both complete arrays.

The third Orchestrator has one mission: execute. It receives the arrays, processes each section independently, and performs the actual transactions in JDE: updates the lines that need to change and adds the new ones.

The role of the LLM in the second Orchestrator’s Groovy

This point deserves honesty, because it’s where things get interesting for anyone working with JDE today.

The Groovy code that applies the comparison rules in the second Orchestrator wasn’t born out of a text editor in isolation. I built it in conversation with an LLM, using very specific prompts: the exact structure of the input JSON, the validation rules in precedence order, the error cases it needed to identify, and the concrete format the two output arrays needed to follow.

The LLM doesn’t know JDE. It doesn’t know what a sales order is or how Business Unit distribution works. But it does know how to build comparison and transformation logic over well-described JSON structures. When you give it precise context, it generates a solid starting point in minutes that you then refine with the business knowledge no model has.

That combination — Groovy plus LLM plus deep JDE knowledge — is probably the most efficient working method I’ve found for complex Orchestrator logic.

What changes when you separate responsibilities this way

There’s an architecture lesson here that goes beyond this specific case.

When an Orchestrator accumulates too much logic because the problem is complex, the instinctive response is to add more steps, more conditions, more layers. The result is an Orchestrator that works but that nobody can maintain in production six months later without anxiety.

Treating each Orchestrator as a unit with a single responsibility completely changes the maintenance equation. When the next business rule change came in on this project, the modification was surgical: it only touched the second Orchestrator. The main one and the third one weren’t touched.

And in an environment where business rules change, that ability to modify without breaking is worth more than any point-in-time technical optimization.

The actual project impact

The sales distribution that previously required manual review for every edge case now processes automatically — including the exceptions. The external system receives confirmation of whether its request was processed or rejected, with the exact reason, without any human intervention.

If you’re working on JDE integration projects where the logic has already outgrown what standard tools can handle, that’s exactly the kind of architecture I design in consulting. Reach out and tell me about your situation.

Leave a Comment