Building Logic Tutorial - Roo Logic

This is Part II of the Tutorial to create a project, from scratch, with Business Logic.  It presumes you have completed Part I - the creation of a project with domain objects, tests data, and a default User Interface.

This tutorial introduces the following key concepts: 
  • @Logic - how to define Logic Classes and Logic Annotations
  • Complex multi-table logic - how your logic addresses complex, multi-table logic
  • Automated Dependency Management - how logic dependencies are detected and automated
  • Object Declarative - how you can use Java for complexity and extensibility
  • Assured Re-use / Integrity - how the Business Logic Engine ensures your logic is re-used
  • Logic Debugging - how verify your logic is operating properly


See here for a summary of the project (repeated below as required).  Recall we will not focus on the the Data Model (which is trivial), or the User Interface.  In fact, we will utilize Spring Roo since it provides default services for those elements, using standard Java.  You can perform a similar tutorial in a wide variety of other IDEs and frameworks.

Introduce Business Logic

Tip: Key Concepts

Watch for boxes like this - they call your attention to the key concepts.
In the prior step, we have created a full application.  Spring/Roo has enabled us to create our domain model and user interface with remarkably little effort.  But we have no business logic - our data is not properly computed or validated.

Configure JPA

As we'll discuss further below, ABL operates as a Hibernate/JPA event listener.  We now configure that.

Open persistence.xml and insert these lines as shown below:

<!-- Activate ABL -->
<property name="hibernate.current_session_context_class" value="com.autobizlogic.abl.session.LogicThreadLocalSessionContext"/>

This activates the Hibernate/JPA to publish transaction events to the abl Logic Engine, which will find and execute your logic as updates are submitted to JPA.

Add the Business Logic Jar

We must also include the abl runtime jar into our project.  Roo is setup for maven, so we insert the following lines into the pom.xml file as shown below (note there are 2 insertion points):

<name>Automated Business Logic repository</name>

     <!--  ABL dependency -->

You will notice a delay when you save the pom file, as STS downloads abl and its dependencies.

Add a Logic Class

We will now add some preliminary - not yet complete or correct - logic:
  1. Create a package ablroodemo.businesslogic under src/main/java
  2. Create a new in this package
  3. Paste or enter the code shown below

package ablroodemo.businesslogic;

import com.autobizlogic.abl.annotations.*;

public class CustomerLogic  {
public void deriveBalance() { }

This should compile cleanly, and look like this when you are finished:

Test Logic

You can test the derivation logic as follows:
  1. Start the Server and Application
  2. Click List All Customers (lower left)
    • note that the Balance values are 0
  3. Click List All PurchaseOrders
  4. Edit the first one
    • use the pencil icon as shown here -->
  5. Set Amount Total = 100, and save
  6. Click List All Customers
    • note that the first Balance is now 100
You can also test the constraint logic:
  1. Set the PurchaseOrder's Amount Total to 1000
    • observe the resultant exception
Feel free to experiment with making other changes.  You will not destroy the test data - it is reloaded each time you start the server.



This example illustrates we can specify logic simply by defining Logic Classes (such as customer) that contain Logic Annotations (such as the sum logic and the constraint).
We have completed a common logic requirement - a multi-table derivation.  We did not need to determine an architectural approach (DAOs?  Service Objects?), nor write and maintain the code.

All we needed to do was specify Logic Classes (such as CustomerLogic), which contain @Logic annotations for logic such as:


Typical Multi-table Logic

The application is certainly not complete - the Order's Amount Total should be computed, not entered by the business user.  We recall our requirements:


Some notes...

The customers balance represents the sum of the ready orders that are unpaid

Compute amountTotal

Requirement 3 states that PurchaseOrder.amountTotal is the sum of the LineItem's amount.  To define this logic, we simply add a similar @Logic annotation:

  1. Create a PurchaseOrderLogic class in the businesslogic package

    • Add the import: import com.autobizlogic.abl.annotations.*

  2. Paste in the code shown at right

public void deriveAmountTotal() { }

Multi-table Chaining

Without any further specification on our part, our two rules chain:
  • altering a LineItem.amount adjusts the PurchaseOrder.amountTotal..

  • which chains to adjust the Customer.balance, per the prior logic
Now, any change to LineItem.amount will adjust our amountTotal.  You can start the application and verify this, or just proceed with the remaining logic.  The callout at the right observes how this new logic automatically chains with the existing logic.

In fact, you can try a number of tests - the business logic is automatically re-used overall all of these Use Cases:
  • Delete a Purchase Order
  • Delete a LineItem
  • Insert a new LineItem
  • Change LineItem amounts
Note you'll manually enter the LineItem.amount.  This is clearly incomplete, and we'll address it momentarily.

Declaring sums and counts is a fundamental pattern of business logic.  Your existing data must be correct for proper results.  Let's briefly consider how:
  • Reloaded test data: In our case, we are reloading our test data each time.  This load process is layered on Hibernate (not in the Controllers), so our reloaded data reflects our new logic -- sums and counts (and other derivations) will be properly initialized.  (This will be apparent after the amount derivations are defined in the next section).
  • Existing data: If you are using existing data, you need to initialize the existing data to conform to your new rules.  ABL provides Recompute to automate this process.

Compute amount

Of course, LineItem.amount is not supposed to be entered directly.  As stipulated in Requirements 4 and 5, it is derived as the price * quantity, where the price comes from the Product.  We define that logic as follows:

  1.  Create the LineItemLogic class as above

    • Add the import: import com.autobizlogic.abl.annotations.*

  2. Paste in the following:

@Formula("productPrice * qtyOrdered")

public void deriveAmount() { } 

public void deriveProductPrice() { }

Cascade Option

We could have derived amount as product.productPrice * qtyOrdered, but that would instruct the system that subsequent changes to the price were to be cascaded to previously entered LineItems.  

That is not the correct business objective, so we utilize the @ParentCopy logic annotation.

Refine Customer balance

As we noted above, the Customer's balance is not exactly the sum of all that customer's orders - Requirement 2 states we only want to sum the unpaid and ready orders.  To define that logic:

  1.  Edit your CustomerLogic class, and alter the balance like this:

("purchaseOrders.amountTotal where paid = false and ready = true")

Add the checkCredit Constraint

Finally, Requirement 1 means we need to ensure that the resultant balance does not exceed the creditLimit. To implement this requirement:

  1.  Still in CustomerLogic, ensure that the resultant balance does not exceed the creditLimit by pasting in the following:

@Constraint(value="balance <= creditLimit", 

    errorMessage="Customer {name} exceeds credit limit")
public void checkBalance() { }

Executable Requirements

We are virtually done, so let's take stock.  Observe that the @Logic Annotations below correspond virtually identically to the requirements (ignoring #6 which we'll address later):

500 lines of code

These 5 simple @Logic annotations represent 500 lines of code.

The key elements are described in the following sections.

Automatic Re-use across Use Cases

These requirements are enforced over all the Use Cases that touch this data, including:
    • Test data loading (note the Customer balances are now properly initialized)
    • Adding and changing new PurchaseOrder and their LineItems
    • Making an PurchaseOrder ready, or paid
    • Reassigning a PurchaseOrder to a different Customer
    • Changing LineItem quantities, or Products
    Not only does this reduce the implementation effort, it improves quality since you can't "forget" to invoke logic for a corner case (e.g., reassign PurchaseOrder increases new balance, but does not decrease old balance).

    Dependency Management - alter Item Product and Quantity

    Execute this scenario with freshly loaded data:
    1. From List All Customers, observe that Bravo's Balance is 60
    2. From List All LineItems, edit the row for Product Hammer 10, Order 60 Please
      • Change the Quantity = 2Product = Drill 315, and save
    3. From List All Customers, observe that Bravo's Balance is adjusted to 680

    Dependency Management

    Analogous to a spreadsheet, the system recomputes data when referenced attributes are changed.  

    Dependency management can chain, including across tables, thereby automating complex multi-table transactions.
    The system performed this complex, multi-table transaction by automating dependency management:
    • Analyze the transaction to check for alterations to attributes referenced by rules
      • re-execute these derivations, 
      • but prune (skip) derivations where referenced attributes are not changed
    • Chain: see if any derived attributes are referenced in still other attributes - process as above
    So we can follow the chain of logic dependencies for our change to the LineItem Quantity / Product:
    1. Since LineItem.productPrice is derived as @Copy(Product.Price), and our Product (foreign key) has changed, update the productPrice
    2. Since LineItem.amount is derived as qtyOrdered * productPrice and our productPrice has changed, this is recomputed
      • This illustrates dependency-based ordering - derivations are performed in an order reflecting their dependencies
    3. Since PurchaseOrder.amountTotal is derived as @Sum(LineItems.amountTotal) and the amountTotal has changed, this is recomputed
      • This illustrates that multi-table chaining - derivations such as sum reference data in related objects; when these are changed, the derivation is updated (more on performance in a moment)
    4. Similarly, since Customer.balance is derived as @sum(PurchaseOrders.amount where paid=false and ready=true), the changed amount causes the system to adjust the balance by the change in the amount
      • This is not quite trivial - the old LineItem was for 1 Hammer @ $10, now replaced by 2 Drills @ $315 - a difference of $620 (hence the balance adjustment).

    Automated Dependency Management confers significant value in agility and TCO:
    • Development: the bulk of your business logic code is typically dependency management.  As illustrated by this example, the 5 annotations replace 500 lines of java code

    • Maintenance: maintenance typically is more about the archaeology of deciphering dependencies so new code can be inserted correctly.  With automated dependency management, you simply add/change your logic, relying on the automated dependency management of the Business Logic Engine to optimize and order the resultant logic.

    Logic Execution, Debugging and Performance

    Plug-in Architecture

    Event-based logic injection means
    • No recoding - in this tutorial, there was no need to instrument Spring Web Apps to invoke logic

    • Assured re-use / integrity - logic execution is thus assured

    Hibernate/ORM Integration

    The Business Logic Engine operates not by your direct call, but rather by listening for Hibernate/JPA events.  

    Logic Debugging

    As developers, we spend a considerable amount of time staring at the screen, wondering "what is it doing?".  As developers, we designed business logic for transparent execution:
    • Logging: the Business Logic Engine can be configured to generate log tracing for every rule that fires, with full depiction of the Domain Object attribute state, and logic chaining (nesting)

    • Debugging: for Logic Methods (introduced below), you can use your debugger to stop in rule execution, examine variables, step into/over etc.

    • Test Tools: we provide tools you can use to see all changes to your database, and verify proper changes as jUnit asserts; these can assist in Test Driven Development
    For more information, please see Logic Debugging.

    Logic Performance

    Logic Execution is optimized to prune and optimize database access.  For example, aggregate processing uses adjustment logic - 1 row updates, rather than reading all the aggregated data (particularly important for chained aggregates).

    Object Declarative - complexity and extensibility

    Logic Annotations are very powerful.  You will find you can address most business problems in an easy and natural manner.

    But surely not all.  We have therefore designed ABL so that in addition to declarative annotations, your Logic Classes can include Logic Methods -  Java / Groovy Methods to:
    • address complex logic, including the utilization of your existing Java libraries

    • provide Extensible Logic - build new generic services
    The last concept is particularly important.  The full download includes the BusLogicExt project, a library of such extended logic.  This is provided both for your use (these represent the automations of patterns we have seen), and as an illustration of how to build your own extensions (for patterns you detect).

    Dependency Management is fully supported for Logic Method derivations, through byte code analysis to detect references.

    We will now illustrate this to address the familiar pattern of auditing: whenever the customers credit limit is altered, we want to insert a row signifying that fact.  

    Install BusLogicExt

    BusLogicExt contains just such a rule.  We install it into our project as follows:
      1. Download buslogicext-2.0.6.jar 
      2. Create a lib folder in your project and copy the jar there
      3. Add the jar to project using Project > Properties, and...
        1. Java Build Path > Libraries > Add Jars > (BusLogicExt.jar)
              and (since it's a web app) your ...
        2. Deployment Assembly > Add > Archives from Workspace > Add > (BusLogicExt.jar)

    Specify Auditing Logic

    We add our logic to the CustomerLogic class.  To refer to the Customer domain instance being updated, we need to insert the following code.  The Business Logic Engine uses the annotation to inject the domain instance object prior to calling your logic, so that your Logic Methods can issue code as as customer.getCreditLimit():

    Customer customer// injected by BusLogicEngine

    Customer customerOld;

    Similarly, we need to access some information about the state of transaction (also injected):

    LogicContext logicContext;

    The Logic Method can be entered or pasted into CustomerLogic:

    public void actionAuditCustomer() {
       if (logicContext.getInitialVerb() == Verb.UPDATE &&
           customer.getCreditLimit().compareTo(customerOld.getCreditLimit()) != 0) {
        System.out.println("Inserting employee audit record");
        Object audit = BusinessLogic.insertChildFrom(CustomerAudit.class, logicContext);

    You will need to resolve various imports.  Use STS services, or paste in the following:

    import ablroodemo.domain.Customer;
    import ablroodemo.domain.CustomerAudit;

    import com.autobizlogic.abl.annotations.Action;
    import com.autobizlogic.abl.annotations.Constraint;
    import com.autobizlogic.abl.annotations.CurrentBean;
    import com.autobizlogic.abl.annotations.LogicContextObject;
    import com.autobizlogic.abl.annotations.OldBean;
    import com.autobizlogic.abl.annotations.Sum;
    import com.autobizlogic.abl.businesslogic.BusinessLogic;
    import com.autobizlogic.abl.logic.LogicContext;
    import com.autobizlogic.abl.logic.Verb;

    Whether you entered the code directly, or copied from this page, it should look something like this:

    Note we've inserted a breakpoint at line 42.  If you do so, you will see the debug information (the customer row contents) if you select the injected variables.  You will also note that the breakpoint is hit multiple times during the Loader process.  (This is a useful feature - your test data is subjected to possibly new business logic each time you start the server).

    We can now test our logic as follows:
      1. Start the Server, Run the app
      2. List All Customers, Edit the first Customer and alter the Credit Limit, and save
      3. List All Customer Audits, and notice the row there

    Next Steps

    Thanks for going through the Tutorial!  Typical next steps:
    • Download the full product - it's open source, and free for development

    • Review the Case-based Training - this tutorial is the "Hello World" of business logic... the training shows that automation scales to complex problems such as a Bill of Materials Explosion, or the allocation of a Payment to a set of outstanding Orders.

    • Review the system Architecture, particularly how it confers a Rich, Active, Declarative Object-oriented architecture for your systems

    • ABL supports advanced services for administering your logic, including

      • Dynamic Logic enables you to reload changed logic without restarting your JVM

      • Recompute services enable you to verify whether your existing data conforms to your logic annotations, and (where possible) to recompute your data per the logic annotations

      • Logicdoc to capture requirements transparently for sharing with Business Users, with full traceability into the underlying logic
    Val Huber,
    Apr 17, 2012, 10:42 AM
    Val Huber,
    Mar 31, 2012, 5:41 PM