PL/SQL Modernization & Business-Logic Extraction

ModernLift · ·12 min read
Part 4 of 8

PL/SQL modernization is the work of recovering the business logic buried in Oracle packages, procedures, functions, and database triggers and surfacing it as explicit, documented domain services. It begins with discovery — reading the PL/SQL to capture what it actually does as a living specification — then separates genuine business rules from database plumbing, decides what to lift into application services versus keep in the database, and rebuilds each rule behind a stable interface, proven equivalent to the original before it carries live traffic. The goal is not a line-by-line translation of PL/SQL; it is making the implicit logic explicit, testable, and owned.

Part 3 ended on a hard dependency: you cannot choose between build, buy, and re-platform until you know what your PL/SQL actually does. This part is about that recovery — the first concrete act of modernization on a Forms estate, and the one that determines whether everything after it succeeds. It is also the part most projects skip, which is why so many of them rebuild a confident, wrong version of the system.

Why this is the center of gravity

Part 2 established the structural fact: in a typical Oracle application, the substantial business logic lives in the database — packages, procedures, functions, and triggers — not in the screens. That logic has three properties that make it the hardest and most valuable thing to modernize:

  • It is undocumented. The rules were encoded over years, in code, by people who understood them at the time. The documentation, where it exists, has drifted from what actually runs.
  • It encodes real edge cases. The branches you do not understand are usually there because something went wrong once and someone fixed it. They look like cruft and behave like requirements.
  • It is shared. A single package is called by forms, reports, batch jobs, and integrations. Its behavior is a contract with multiple consumers, even though nothing declares it as one.

Recover this well and every downstream decision gets easier and safer. Recover it badly — or skip it and rebuild from the screens — and you ship a system that looks right in a demo and is wrong in the cases that matter.

Step one: discovery, not translation

The instinct, especially under time pressure, is to reach for a tool that converts PL/SQL into a modern language and call that modernization. Resist it. A mechanical conversion transliterates the code: you get Java or C# or TypeScript that still has the shape of the original PL/SQL — the same procedural structure, the same implicit assumptions, the same reliance on database behavior that may not exist on the new stack. You have changed the language and preserved the problem. Nobody understands the result any better, and now it runs outside the engine it was written for.

The work that pays off is discovery: reading the PL/SQL to recover what business rule it expresses, independent of how it is written, and capturing that as a living specification — a description of behavior that stays tied to the code it came from. This is where AI-assisted analysis earns its place, and only here: it can deep-read large bodies of PL/SQL far faster than a human can, producing a first-draft map of the packages, their call graphs, their consumers, and the rules they encode. That draft is then reviewed by engineers and, crucially, confirmed against the system’s real behavior. AI is how the reading scales; it is not left to decide what the system does. The code shows what runs; people and parity tests confirm what it means.

Step two: separate the rule from the plumbing

Not everything in a PL/SQL package is business logic. A lot of it is plumbing — cursor management, error handling, the mechanics of moving data around. The discovery output has to separate the two, because they have different destinies:

  • Business rules — how a price is calculated, when an order is valid, what makes an account delinquent, which approvals a transaction needs. These are the asset. They get surfaced as explicit, named, testable services.
  • Plumbing — the procedural machinery that exists to make PL/SQL run inside Oracle. Much of it disappears on a modern stack, replaced by framework features, or is rewritten idiomatically rather than carried over.

This separation is also where you find the duplication and the contradictions Part 2 warned about: the rule that lives in a form trigger and in the package it calls, the two subtly disagreeing. Discovery surfaces the conflict; a human decides which one is correct, often by asking the business. That is a decision a converter cannot make and will instead silently preserve as a bug.

Step three: decide what lives where

A central PL/SQL modernization question, and one people answer too quickly: should a given rule stay in the database or move into application code? There is no blanket answer, and a dogmatic position either way is a mistake. The honest decision is made rule by rule:

  • Keep at the data tier when the logic is fundamentally about data integrity and must hold regardless of what touches the table — referential rules, constraints, set-based operations that are genuinely more correct and efficient close to the data. A database trigger enforcing an invariant is often there for a good reason.
  • Lift into an explicit domain service when the logic is a business process, when it orchestrates across entities, or when it is shared across channels and needs to be tested, versioned, and reused independently of any one schema. Logic of this kind is trapped, not housed, in the database — exposing it as a service is what lets a new web UI, a partner API, and a batch job all call the same proven rule.

The aim is a clean separation: a modern application layer that owns business processes as services, sitting over a data tier that owns data integrity — rather than the original arrangement where everything is tangled together in PL/SQL and the boundary between “what the data is” and “what the business does” was never drawn.

Step four: rebuild behind a stable interface, prove parity

Once a rule is understood and its home is decided, it is re-implemented — and then held to the same bar as everything else in a modernization: it must behave identically to the original before it carries live traffic. Each surfaced service gets a stable interface, and the strangler facade routes callers to either the legacy PL/SQL path or the new service. Functional-equivalence testing runs the new service against the original across real inputs — including the edge cases discovery surfaced — and compares results, including data effects, not just return values. Only when a service matches does it take over. This is what makes extracting logic safe: you are never asked to trust that the recovered rule is right; you prove it against the system that has been running it for years.

What isn’t worth extracting

Not all PL/SQL is worth extracting. Discovery routinely finds dead packages for retired products, reports nobody runs, and branches that have not executed in years. The right move for those is retire, not rebuild — and saying so is part of an honest engagement. Equally, a stable, well-bounded body of database logic that is not blocking anything and carries no compliance or skills risk may be fine to leave in place for now. The goal is not to evacuate the database on principle; it is to make the logic explicit and owned so that every subsequent choice — keep, lift, or retire — is made with eyes open instead of in the dark.

Where this leads

With the logic surfaced as explicit services, the screens become the comparatively easy half — and you finally have something clean to build a new UI against. Part 5, Oracle Forms to a Modern Web App: SPA + API, takes the most common build target: rebuilding the Forms UI as a modern single-page application over an API layer, mapping blocks and triggers onto components and endpoints, and wiring the front end to the domain services this part recovered — slice by slice, parity-proven, with the legacy application running the whole time.

Frequently asked questions

Can you just convert PL/SQL to Java or another language automatically?
You can transliterate it, and that is usually a mistake. An automatic line-by-line conversion produces code in a new language that still carries the structure, assumptions, and quirks of the original PL/SQL — a procedural blob that no one understands any better than before, now without the database features it relied on. The valuable move is to extract the business rule the PL/SQL expresses and re-implement it as a clean domain service. Translation preserves the form; extraction preserves the meaning.
Should business logic stay in the database or move into application code?
It depends on the logic. Set-based data operations, integrity constraints, and rules that must hold no matter what touches the data often belong in or near the database. Cross-cutting business processes, orchestration, and rules that multiple channels and services share usually belong in explicit application-layer domain services, where they can be tested, versioned, and reused without being tied to one schema. The decision is made rule by rule, on evidence, not by a blanket policy.
How do you recover logic that nobody documented?
By reading the code as the source of truth and capturing what it does as a living specification, then validating that specification against the system's real behavior. AI-assisted analysis can deep-read large PL/SQL bodies far faster than manual review to produce a first draft of the rules, but the output is reviewed by engineers and confirmed against observed behavior — the code shows what runs, and parity testing confirms the recovered rule matches reality before anything is rebuilt against it.
All 8 parts of Oracle Forms & PL/SQL Modernization →