Building Logic Tutorial - 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 from Maven 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



Introduction

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.  We have simply built a standard JSP application.  You can perform a similar tutorial in a wide variety of 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: our domain model and user interface.  But we have no business logic - our data is not properly computed or validated.


Add the Business Logic Jar

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

     <!--  ABL dependency -->
<dependency>
     <groupId>com.autobizlogic.abl</groupId>
     <artifactId>autobizlogic</artifactId>
     <version>2.1.6</version>
</dependency>


<repository>
<id>automated-business-logic</id>
<name>Automated Business Logic repository</name>
<url>http://resources.automatedbusinesslogic.com/maven2</url>
</repository>

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



Configure Hibernate

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

Open buslogictutorial.cfg.xml and replace line 24 (hibernate.current_session_context_class) with the lines shown below:
        

<!-- This is the only setting required to activate ABL -->

<property name="hibernate.current_session_context_class">
         com.autobizlogic.abl.session.LogicThreadLocalSessionContext</property>




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 a Logic Class

We will now add some preliminary - not yet complete or correct - logic:
  1. Right Click on the project, and use New > Class
  2. Specify the package name buslogictutorial.businesslogic (it should be under src/main/java)
  3. Specify the class name as CustomerLogic.java
  4. Paste or enter the code shown below

package buslogictutorial.businesslogic;

import com.autobizlogic.abl.annotations.*;

public class CustomerLogic  {
@Sum("purchaseorders.amountTotal where paid == false")
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. Alter Paid (see cursor position at right)
  3. Observe the effect on Customer Balance
Since logic is specified at the Domain Object level, it is automatically shared across Use Cases:
  1. Alter paid so it is back to the original value
  2. Delete the Order
  3. Observe the adjustment to the Customer balance
Feel free to experiment with making other changes.  You will not destroy the test data - it is reloaded each time you start the server.







@Logic

@Logic

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:

 
@Sum("purchaseOrders.amountTotal where paid = false")



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 Purchaserder.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


@Sum("lineitems.amount")
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() { } 

@ParentCopy("product.price")
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.


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. Observe that Alpha's Balance is 115
    2. In Item # 13, make make these changes...
      • Quantity = 1
      • Product = Drill
    3. Verify the Order is Unpaid (clear the checkbox if required)
    4. Observe that Alpha's Balance is adjusted to 400

    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 adjusted
      • 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 3 Hammers @ $10, now replaced by 1 Drill @ $315 - a difference of $285 (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():

    @CurrentBean
    Customer customer// injected by BusLogicEngine

    @OldBean
    Customer customerOld;


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

    @LogicContextObject
    LogicContext logicContext;


    The Logic Method can be entered or pasted into CustomerLogic:

    @Action
    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 buslogictutorial.data.Customer;
    import buslogictutorial.data.CustomerAudit;

    import com.autobizlogic.abl.annotations.*;
    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:


    You might wish to insert 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
    2. Change the Credit Limit for the first customer
    3. Inspect the Console Log for a line such as shown below

    abl.engine -   |  [CustomerAudit[1] LOGIC] ##INSERT END on = [auditId=1, balance=345.00...


    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
    ċ
    buslogicext-2.0.6.jar
    (33k)
    Val Huber,
    Apr 7, 2012, 12:08 PM
    Comments