Architecture‎ > ‎

Business Logic Components

Business Logic Components are Groovy/Java class files (Logic Classes) that declare Logic Annotations for a Domain Object. Business Logic Components parallel the Java Domain Objects - you either place them in a standard location, or control their location.

This section explains how to create them.


   


Create a Logic class

Follow the guidelines in the sub-sections below (illustrated above).  Here is a sample you can copy into your project.


Package Name

We recommend using the package name of your DomainClass, but replace the <data> node of the package name with businesslogic, for example:

  • buslogicintro.businesslogic.orderentry contains the Business Logic Components for
  • buslogicintro.data.orderentry Domain Objects

You are not restricted to this - it is merely a convention to enable defaulting.  Alternatively, you can configure ABL to designate your exact package structure.


Class Name

We recommend using the default name: <DomainObjectName>Logic, for example

  • CustomerLogic is the Business Logic Component for
  • Customer      Domain Object



Logic Class Content


Instance Variables

Create Instance Variables for the current/old Domain Object instances. These will be initialized by the Business Logic Engine prior to calling your Business Logic Rule methods, enabling reference to your domain data.

  1. Create these with the required annotations:
    • @CurrentBean
    • @OldBean
  2. We recommend the (rather obvious) names of <DomainObjectName> and old<DomainObjectName>, for instance:

    @CurrentBean
    private PurchaseOrder purchaseOrder;

    @OldBean
    private PurchaseOrder oldPurchaseOrder;
  3. The Object Type must, of course, match your Domain Object

Both variables are optional, but obviously if you don't have at least one, your logic won't be able to do much. There is a minuscule performance penalty for initializing these variables if you don't need them.


Logic Context (@LogicContext)

In addition to domain instance variables, the system injects @LogicContext per your request:

@LogicContextObject
public LogicContext logicContext;


This object contains several methods useful in your logic declaration, including
  • services to insert, update and delete objects with logic execution
  • reference to the current domain objects (useful for generic code)
It is a common practice to extend Logic Classes from a BaseLogic class that provides inherited services.  You may find it convenient to declare LogicContext in the BaseLogic class.  You may also find it convenient to pass the LogicContext into re-usable logic services.


Logic Methods for Business Logic Rules

Once you have created your Business Logic Component, build Logic Methods that declare your Business Logic Rules.  

The basis of Business Logic Development is the Core Rules. Designate your Business Logic Methods via annotations to specify the rules summarized below.


 Rule       Description Example

 FormulaDerive attribute value using other attributes in that class, or parent class (changes are propagated) @Formula("productPrice * qtyOrdered")
 public void deriveAmount() { }

 Parent CopyDerive child attribute value from parent attribute value (changes are not propagated)

 @ParentCopy("product.price")
 public void deriveProductPrice() { }

 SumDerive parent attribute value by summing designated child attribute, with optional Child Qualification Condition @Sum("purchaseorders.amountTotal where paid = false")
 public void deriveBalance() { }

 Count

 
Derive parent attribute value by counting designated child rows, with optional Child Qualification Condition  @Count("purchaseorders where paid = false")
 public void deriveNumUnpaidOrders() { }
 ConstraintMulti-attribute expression of class/parent attributes that must be met for a transaction to succeed (else exception is thrown); can execute as transaction rows are processed, or at commit time after all rows are processed.

 @Constraint("balance <= creditLimit")
 public void constraintCreditLimit() { }

 Action

 and

CommitAction
Invoke Java/Groovy action (typically from a library of extended rules); can execute during or after row processing

 @Action
 public void auditSalary() {
    if (employee.baseSalary != employee_old.baseSalary)
       BusinessLogic.insertChildFrom(
          EmployeeAudit.class, 
          logicContext);
}


Key Notes:

Logic is unordered
The Business Logic Engine invokes your rules automatically during transaction processing, ordered by their dependencies

Old Values
Formulas, Constraints and Actions can reference not only attribute values, but old attribute values, enabling you to specify state transition logic

logicContext
Provides access to runtime services and state, such as logicNestLeveluseCaseName, etc.

Logic Design Patterns 
Most requirements can be met using these patterns which leverage Forward Chaining; see the Tutorial.

Extensibility
Rules can invoke Java/Groovy methods, enabling you to meet requirements not solved by Core Rules.  Judicious design can result inreusable logic, as illustrated by the pre-supplied BusinessLogic services for allocation and copy. 


Logic Method Annotation

You designate methods in your Business Logic Component via annotations.  The annotation is always the name of the rule (without spaces) in the table above.  Additional tags are explained in the documentation for each rule.

Your IDE provides pick-lists of annotations.  For example, <option>+Space in Eclipse provides this list:

Logic Method Code

The semantic deriving / constraining data can be expressed in either of two ways:
  • in the annotation
Most logic expressions are simple, so formula / constraint Logic Annotations enable you to specify the expression in the annotation itself.  For example, see the productPrice derivation below; several other examples are illustrated here.  Since annotations must apply to method, you must supply one which can be null as shown in the link.
  • in the Logic Method
For more complex logic, you can provide the logic in actual Java/Groovy code.  This provides familiar services for if/else, looping, local variables and so forth, and importantly the ability to invoke existing Java/Groovy methods.  See deriveAmount, below.  Such extensibility enables you to introduce new services to automate patterns you detect, as illustrated in the link.

In either case, the Logic Engine processes your declarations, including optimizations and dependency management.



Logic Method Name

There are no restrictions on Logic Method Names.  There are conventions you might want to consider:
  • Derivation Rules: use derive<attributeName>    
Derivation Rules (sum, count, formula, parent copy) require the system to know the attribute name.  It is simplest and recommended to use the convention shown (e.g., deriveBalance()). 

   @Sum("purchaseorders.amountUnPaid where isReady = true")

   public void deriveBalance() { 

If you do not, you will need to inform the system of the attribute name with the annotation tag AttributeName which is otherwise optional, as shown below:

  @Formula (attributeName = "notes")

     String setNotes() {


  • Other rules
The only consideration here is your conventions.  We have prepended the rule type to the name (e.g., constraintInvalidNote), but this is not necessary and perhaps even redundant in view of the annotation preceding it.

All logic methods must be declared public.

Logic Method Javadoc

This is optional but highly recommended.  Note:
  • The "summary" (the first sentence, up to the ".") is used by Logicdoc as the rule summary

  • For sum/count: typically omit
the system will default the annotation definition if you omit the Javadoc; this is typically quite sufficient, so we typically omit the Javadoc for sums/counts in our samples.  You can, of course, adopt a different convention.

  • Others: summary recommended
In most derivations, there is the "kernel" of logic that is useful as the Javadoc summary.  This typically omits null checking and other things of marginal value.  See the example below.


/**

* @return amountTotal, less 5% for Platinum.

*/

@Formula (persistent = false)

BigDecimal deriveAmountDiscounted() {

    if (purchaseorder.amountTotal == null)

        return 0

    else {

        if (purchaseorder.customer == null) {  // if cloning, no customer yet

            return purchaseorder.amountTotal

        }

        BigDecimal discountedAmount = (purchaseorder.customer.customerLevel=="P") ?

            purchaseorder.amountTotal * 0.95 :

            purchaseorder.amountTotal

        if (discountedAmount != purchaseorder.amountTotal)

            System.out.println("We gave a Platinum Discount")

        return discountedAmount

    }

}



Other Methods

You can also build procedural methods - see Business Logic Components can invoke Java/Groovy Methods.  These should not include a Logic Method Annotation.



Execution

Logic Analysis

The Business Logic Engine listens to Hibernate update events.  The updated POJO class name is used to locate your Business Logic Component if it is not already loaded, it is located either by the parallel package naming convention, or by configuration in ABLConfig.properties.  

Logic Analysis next introspects the class to locate your Logic Methods via their annotations, and then performs byte code analysis to determine all the referenced POJO attributes.  These used to build the dependency graph, used to define pruning and a proper execution order.

Execution

Unlike procedural programming where your methods are called explicitly, Business Logic Rule methods are invoked by Business Logic Runtime, in response to inserts/updates/deletes done by client applications. Important observations:

  1. You can set breakpoints in the Business Logic Rule methods

  2. Ordering is determined based on the Logic Analysis dependency information described above

  3. But, such calls are not always made. The Business Logic Engine will not call a Formula if, for example, all its local attributes are unchanged.

  4. Business Logic Components can be invoked multiple times per transaction, on the same row.
    For example, saving n Lineitems for a Purchaseorder would execute your Business Logic Component n times. On call "i", the old values pertain to call "i-1".