The Automation Gap
The illustration below observes that while there is significant automation for important parts of an application (such as UI frameworks or rules engines), the transaction logic element is presumed to be manually coded. This is a problem due to:
- Time and cost: save/submit transaction logic is often 40% of the development effort for update-oriented applications, so directly affects business agility and application TCO
- Complexity: some advocate adding logic to domain objects, while others advocate the introduction of a service layer to address complex multi-table logic
Existing Approaches fall short
Attempts to use existing tools appear promising, but fall short:
- Rete engines provide excellent value for running complex rules on a row, and providing End User access. However, they have well-known performance issues in processing sum/count logic, since they result in aggregate queries or read all related data into memory. This is particularly painful when data volumes are high, or there are nested sums/counts such as the example below where the customer's balance is a sum of order totals, but the order total itself is a sum of item amounts. These result in a geometric number of reads
- Process engines are excellent for work flows, but their inherently procedural nature does not enable a declarative approach
- Hibernate provides useful attribute validation; while useful, these fall far short of the typical requirements for multi-attribute validation, and multi-table derivations such as sums and counts
Example: Add Order
5 Simple Requirements
The illustration below depicts a simple screen with a Customer, their Purchase Orders, and the related Line Items. The left side depicts a rigorous refinement of the "Check Credit" requirement in 5 succinct rules, perhaps gleaned from a design session.
Many related Use Cases result in 500 lines of Java code
This diagram illustrates that the screen above obligates a developer to handle a series of related Use Cases shown on the right. Observe that:
- Error Prone: the first job of the developer is to produce the list of Use Cases. Design errors here can be serious compromises in data integrity, leading to incorrect business decisions. Can you spot the missing case?
- Rules sufficient: it is remarkable that the actual processing for each of the Use Cases can be easily inferred from the same rules at the left. For example, the second rule dictates not only that Add Order should increase the balance, but also that the balance should be reduced by Delete Order or Pay Order.
- Significant development effort: handling all of these Use Cases, including their change detection, change propagation, and optimization requires 500 lines of code - examine them here.
Declaring Transaction Logic: 5 simple rules
By contrast, the screen shot below illustrates the complete logic declarations required to implement Add Order and all the related Use Case:
- The upper right window is the Customer Domain Object used by Hibernate. These require no changes to utilize business logic
- The other 3 windows illustrate the complete logic source executed by ABL.
- These parallel the Domain Objects (e.g., stored in parallel packages), later used by the Transaction Logic Engine described below. So, CustomerLogic is the business logic for the Customer domain object.
- Logic is specified in Java/Groovy classes, using an annotated method for each rule (see transparent blue boxes, below). For example, the CustomerLogic window contains the first 2 rules.
This sample problem (Customers, Orders and Parts - shipped in the download) is a simple way to illustrate the use and operation of business logic. The requirement Customer's balance may not exceed their credit limit is addressed as follows:
Far beyond simple single-attribute validations, the requirement is multi-attribute constraints, such as this Customer Constraint:
Constraints are the most familiar form of business logic, but derivations and actions are the most valuable, per dependency / re-use automation noted below. Derivations here include multi-table derivation rules such as
Customer.balance = sum(order.total where paid = false)
- Derivations are multi-table, as in the example above
- Derivations can chain, both to constraints, and other derivations:
Order.total = sum(Lineitem.amount)
Lineitem.amount = quantity * partPrice
Lineitem.partPrice = copy(Part.price)
- Chaining, ordering and dependencies are automatically handled (logic is declarative, somewhat similar to a spreadsheet)
- Derivations can include if/else logic, state transition logic, and reference related data
Referring to the blue callouts to the right in the diagram, there are several important aspects worth noting, discussed below.
Automatic Invocation / Re-use
Unlike procedural programming where you must explicitly call and order your logic, the rules above are automatically invoked by the Transaction Logic Engine (described below). The engine assumes the responsibility to monitor all of your transactions, and apply the logic as required.
As a consequence, the 5 rules above address all of the Use Cases - automatically. This not only accounts for the vast majority of the code savings, but eliminates an entire class of design error, such as neglecting an entire Use Case, or elements of logic within a Use Case.
Such automation is familiar. If you declare a spreadsheet cell to be the sum of a column, the spreadsheet "knows" that it should recompute the cell when any column data is altered, or when a row is added or deleted in the column.
ABL provides exactly the same automation. The
balance = sum (unpaid ordertotals) rule automatically addresses all Use cases that touch the dependent data, including Add Order (balance increases), delete order (balance decreases), pay order (balance decreases), and so forth. Re-use - achieved in procedural approaches only with painstaking design - is automated.
In a declarative approach, you specify what you want rather than how to do it. An important consequence is that the engine can - is obligated - to optimize your logic.
balance = sum (purchaseorders.amountTotal where paid=false) looks very much like a SQL sum. It is not.
Instead, the logic engine's optimizer utilizes adjustment logic, so altering an
amountTotal by X simply adds X to the
balance. This completely avoids the select sum logic for purchaseorders and items, replacing them with a single row update.
Inherent in a declarative approach is that rule chaining is supported (one rule can reference the results of another), and that the system assumes the responsibility for detecting dependencies for correct ordering. So, the rules balance = sum (order.total where paid=false) and ordertotal = sum(lineitem.amount)can be specified in any order.
This is most important during maintenance: in manual code, the principal time is spent in studying the existing code to understand its dependence-based ordering, so new code can be introduced properly.
The Transaction Logic Engine eliminates this costly (and unpleasant) "archaeology" by deducing dependencies using byte code analysis to compute a correct order of operations. So, you need only alter rules to meet the business requirements - automated dependency management is repeated during the next execution.
This diagram depicts the use and operation of business logic; note that:
- You do not directly invoke business logic - neither your annotated methods, nor the Transaction Logic Engine
- Instead, the Transaction Logic Engine plugs in to (listens for) Hibernate events, such as beforeUpdate, beforeCommit etc. When these are detected, the engine loads your logic classes, determines their dependencies, and executes the logic in a correct and optimized order.
Easy to get started - plug-in means no recoding
This "plug-in" approach means you don't have to alter your (or your framework's) existing code to utilize business logic. You simply change a configuration file, and introduce logic classes - at your own pace.
Automation often incurs costs of efficiency, or limitations. The Transaction Logic engine is designed for extensibility:
- In addition to using annotated methods, your logic class can use Java/Groovy methods. This means you can utilize if/else logic, loops, etc as required
- Such Java/Groovy logic can invoke Java/Groovy methods. This enables you to build re-usable services that automate patterns you detect in your applications.
ABL ships with several such extensions. Not only do these illustrate the extensibility approach, they are fully supported elements of the product, and enable the automation of classically complex Use Cases such as a Bill of Materials explosion, a deep copy, or the allocation of a Payment to a set of outstanding orders.
Easy to Integrate into your runtime environment
The plug-in approach means that logic can follow Hibernate/JPA support for different servers (Stand alone, Web Server, App Server), databases (including App Server distributed transaction support), and platforms.
Easy to Integrate into your development environment
Logic fits into your development architecture:
- Logic is specified using your IDE (e.g., Eclipse)
- Logic is represented in classes, so your existing source control procedures are unaffected
- Logic provides extensive logging depicting every rule firing along with system state, and can be debugged using standard debuggers
| Key Technology|| Enables|
| Declarative|| Automated Dependency Management - execution order deduced from dependencies - simplifies maintenance |
Automated Optimization - pruning and adjustment, re-computed on every change
| Domain Based|| Automated Re-use - logic is encapsulated into Domain Objects, so, like a spreadsheet, is automatically applied to all Use Cases that touch Domain Objects. |
| Plug-in|| Easy to get started - just configure and add logi-|
| Extensible|| Complexity Scaling|In many ways, the combination of a Declarative approach based on Domain Data brings the impressive power and simplicity of spreadsheets to business logic.
Strategic Business Advantage
These technology elements result in strategic competitive advantage for your organization:
- Agility: replacing 500 lines of Java code with 5 rules - for 40% of your system - means your business can take advantage of opportunities and respond to change faster. And since rules are transparent to Business Users, they can help spot errors to reduce requirements risk.
- Quality: automated re-use can reduce the common (and painful) scenario often discovered in final tests, where logic elements are not coded into some Use Cases, or some Use Cases are not implemented.
- Performance: pruning and adjustment optimizations can reduce the common (and painful) scenario where systems ran fine in development for small database sizes, but require major rewrite to deal with production size data.