Architecture

This describes the Automated Business Logic Architecture, illustrated below.  Please also see this Architectural Perspective, describing the considerations and objectives that drove the architectural decisions.






Hibernate Persistence - architectural freedom

Hibernate has become a de facto standard, providing runtime services for reading/writing your Java domain objects, maintaining an object oriented interface to your data. A key design goal of Business Logic is seamless integration into your existing system, so no changes are required to the interfaces provided by Hibernate to read/write data.

This Hibernate integration means you not only gain the agility of @Logic, but you can also freely mix-and-match architectural components as described below.




Java Web Applications are typically developed using one of many available frameworks for User Interface Components and Application Code invocation.  It is common that these support Hibernate/JPA, which thus enables you to utilize Automated Business Logic.  See the title link for a list of examples.


Deployment - Web Servers, App Servers, Cloud

You have a virtually unrestricted choice in deployment architectures, ranging from Web Servers, to App Servers, to Cloud-based deployment.  You can see several examples of Cloud deployment here.


Services Integration

It is straightforward to integrate Business Logic with your Services Oriented Architecture, either for message processing or message invocation.


Domain Objects

No POJO changes

Automated Business Logic places no special requirements on your POJOs.  They do not need to inherit from a special class, or require any special coding.  This means there is no interference with other intended uses of your POJOs, such as serving as Data Transfer Objects.
Your data is represented as Java Domain Objects. You configure Hibernate as usual to register these, enabling you to perform read/update operations. Your Domain Objects are typically POJOs - Plain Old Java Objects: Java Beans, with getters/setters for attributes and related data. So, as shown in the diagram, an order entry system might have a Customer, an Order, and so forth.

In addition to the POJOs, you define mappings and semantics that enable Hibernate to enforce proper semantics. These can take the form of either hbm xml files, or annotations on the Domain Objects.

ABL also supports dynamic objects: database objects defined as maps rather than POJOs. This allows for database schema changes at runtime.


Business Logic

No API Changes

Your logic is invoked by the Business Logic Engine in response to Hibernate update events.  There is no change to the Hibernate API for reading and writing data.
The use of Business Logic has, for the most part, no impact on how you call Hibernate, or build your Domain Objects. To engage Business Logic, you simply need to

  1. Configure Hibernate to invoke the Business Logic engine, and

  2. Build Business Logic Components in which you specify Business Logic Rules annotated methods that govern derivations, constraints and actions for your Domain Objects. See deriveAmount in the screen shot below.

Unlike procedural code, such specifications are declarative:

  1. Your logic is typically expressed in the annotation; more complex logic can be expressed in the method

  2. You do not directly invoke this logic. That is the responsibility of the Logic Engine. This provides declarative encapsulation, assuring your logic is enforced over all your transactions (e.g., interactive or message based).

  3. You do not order this logic - also a responsibility of the Logic Engine. This is particularly important for iterations or maintenance cycles, since you not need to reorder your logic with respect to new dependencies

  4. The logic is system-optimized to minimize and eliminate database overhead.

The screen shot below depicts using Eclipse to declare a Business Logic Rule within a Business Logic Component. The rule derives an attribute AmountUnPaid = AmountTotal-AmountUnPaid (with some null checking):

  • Like most Logic Methods, it is rather simple and short - not unlike a spreadsheet cell formula

  • As further described below, your component contains member variables for Domain Object access; these are initialized prior to the logic invocation (e.g., purchaseorder) by the Business Logic Engine

  • Business Logic Components can invoke Java/Groovy Methods


Business Logic Rules

Business Logic Rules declare how to derive, constrain or process Domain Object data. Business Logic rules are specified as annotated methods in Business Logic Components. In the diagram above:

  • deriveAmountUnPaid is a Formula Business Logic Rule; the method code specifies how to compute the Amount attribute
  • deriveAmount is another Business Logic Rule; it contains no code - it is completely defined by the annotation

Click on the title to see a summary and detailed description of the Business Logic Rules. In particular note that the Business Logic Rules are inherently multi-object, that is, they enable you to reference related data:

  1. Parent Reference: objects on the "many" side of a one-to-many relationship can reference related parent data, such as
    • Amount = part.price * lineitem.quantity

  2. Children Reference: objects on the "one" side can aggregate their child data, such as
    • Derive Customer.Balance = Sum(orders where order.isReady)

Business Logic Components

Business Logic Components are Groovy or Java class files that declare Business Logic Rules for a Domain Object. Business Logic Components parallel the Java Domain Objects - you either place them in a standard location, or configure their location. In the diagram above, PurchaseorderLogic is a Business Logic Component for the Domain Object Purchaseorder.


Java Business Logic Components

While the screen shot above illustrates that you can use Groovy to build your logic, you can, of course, use Java. Further, as shown in the screen shot, you can use both.


Groovy Business Logic Components

You can also specify your logic in Groovy. It is worth reviewing the Groovy description, but the following points are particularly salient for Business Logic:

  1. automatic type conversions dramatically simplify expressions. Most of your logic is simply expressions...
    • Groovy: lineitem.price * lineitem.quantity
    • Java: lineitem.getPrice().multiply( new BigDecimal(lineitem.getQuantity()) )

  2. syntax simplification makes your logic easier to read...
    • Groovy: allocation.purchaseorder.amountUnPaid
    • Java: allocation.getPurchaseorder().getAmountUnPaid()

Model Integration

Observe Automated Business Logic does not require a model.  Or, to put it differently, the Model is the introspectable classes and their annotations, for both Domain Objects and Business Logic.  

If you desire to build additional tooling, you can 

  • extend this approach with additional annotations, used in conjunction with Introspection and/or doclets

  • or, utilize a modeling framework (such as Eclipse EMF) that incorporates Java objects

Built in standard IDE, such as Eclipse

You build your Business Logic Components using your favorite IDE (Eclipse TM, NetBeans TM, IntelliJ TM, etc...). Such tools provide:

  1. Code Completion - for example, pressing Control+Space may provide a list of choices (methods, variables etc)
  2. Syntax highlighting
  3. Error indication
  4. etc...

Logic Debugging

Since your logic is executable, you can use the debugger to set breakpoints, inspect variables, etc. Extensive logging is provided so you can see exactly what rules fired, and what SQLs were executed, as the transaction executes.


Performance

The Logic Engine is designed to minimize the cost and number of SQL operations. For example:

  1. Parent Reference Pruning: SQLs to access parent rows are averted if other other (local) expression values are unchanged.

  2. Cascade Pruning: If parent attributes are altered that are referenced by child logic, the system cascades the change to each child row.  If the parent attribute is not altered, cascade overhead is pruned.

  3. Adjustment: for persisted sum/count aggregates, the system does not issue select sum aggregate queries. Instead, it makes a single-row update to adjust the parent, based on the old/new values in the child.   Aggregate queries can be particularly costly when they cascade (e.g., the Customer's balance is the sum of the order Amount, which is the sum of each Order's Lineitem Amounts).

  4. Adjustment Pruning: adjustment only occurs when the summed attribute changes, the foreign key changes, or the qualification condition changes.  If none of these occur, parent access/chaining is averted.

Extensibility

Most Business Logic specifications are simple spreadsheet-like expressions:

  • price * quantity
  • Sum(orders where isPaid=true)
  • balance < creditLimit

Beyond such expressions, significant additional power is provided since your logic can invoke Java or Groovy methods. You can use existing Java/Groovy methods, or utilize Extensibility:

  • Functions: you can build methods that return a scalar value for use in logic, such as Date Functions

  • Logic Types: you can build new logic (rule) types. We have provided several that are both useful, and illustrate how to build such extensions

  • Factories: you can supply your own factory methods to
    • Choose the Business Logic Component for a particular update (e.g., Retail Orders vs. Wholesale Orders)
    • Maintain your own LogicContext


Business Logic Runtime

The classes in this jar file are responsible for executing your Business Logic, that is, invoking the logic shown above.  It operates as described below.  The Business Logic Runtime, also referred to as the Business Logic Engine, is supplied as a jar file, which can execute inside or outside a container.


Operates as Hibernate Listener

It is important to understand that you do not call the engine directly.  Instead, as shown in the diagram, you configure the engine to listen for Hibernate events.  This preserves existing APIs, so that you don't have to alter your application to use Business Logic.
 
Hibernate Events enable you to supply handlers that are invoked on various Hibernate actions such as retrieval, update and begin/end transaction. Spreadsheet Business Logic uses these to execute your Business Logic.

The Logic Engine analyzes what Domain Objects were changed in the event, and invokes the relevant Business Logic. As illustrated in the Log, there are multiple phases as described below.

Submit Phase - Logic Analyzer

Errors raised in Commit Phase

Note that Constraint Exceptions are not thrown at this stage - they are thrown in the commit phase.  This may affect default framework error handling and notification.
Your application / framework makes normal write / write calls to Hibernate.  Your logic is not processed at this time - pre-event listeners simply build a list of changed data (later processed in the Logic Phase, below).

When an pre-event is received, the Logic Engine interrogates the event to determine the Domain Object that is being changed (inserted, updated or deleted).  If the corresponding Business Logic Component has not been loaded, it loads and analyzes it as described below.

Discovery

Your Business Logic Component is discovered in one of two ways:
  • Convention: buslogicdemo.data.Customer =>buslogicdemo.businesslogic.CustomerLogic
If the last node in your package name is "data", the system replaces this node with "businesslogic", and looks for a class named identically to the POJO, suffixed by "Logic".  This convention is used in BusLogicDemo, where the POJO buslogicdemo.data.Customer has a Business Logic Component named buslogicdemo.businesslogic.CustomerLogic


Once the component is discovered, the Logic Analyzer then discovers your Business Logic Methods, identified by annotations.  These are located using Java introspection.

Dependency Analyzer

The Logic Analyzer next invokes the Dependency Analyzer to build a dependency graph of the execution order that reflects dependencies between logic rules, plus other inter-rule reference information that enables rules to be pruned based on the data actually changed.  The Dependency Analyzer operates by scanning the byte code for the logic methods.


Logic Phase

After all the submitted rows are processed as described in the Submit Phase, the next part of Hibernate commit processing is to call doBeforeTransactionCompletion.  This is handled by the Business Logic Engine when your application issues a commit, which invokes your logic as described in the sub-sections below.

Business Logic Component creation

An instance of your Business Logic Component is created.  

Multiple Instances

Note multiple instances of the same class may be created in the same transaction, even for the same row (e.g., repeated adjustments of a Purchaseorder for multiple Lineitems).  

Your logic should therefore not presume that instance variables retain values over calls.

Instance Variable Initialization

illustrated above (e.g., PaymentPurchaseorderAllocation). These system-created proxy objects enable your logic to:
  1. Reference current/old Domain Object values

  2. Access related data (via Domain Object accessors, such as purchaseorder.customer)

  3. LogicContext contains information such as session so you can Invoke Hibernate services to read data, get Hibernate metadata, get the Forward Chain nest level, etc. 

Transaction Analysis

The Transaction Analyzer uses Hibernate event state to determine which attributes have actually been altered by the client.  This information is used for pruning, and to activate forward chaining. 

Business Logic Method Invocation

The Business Logic Engine determines a correct order for inter-dependent derivations using information gathered by the Dependency Analyzer.   It then invokes your Business Logic Methods.  Observe that:
  1. You can use your IDE's debugger to set breakpoints, review variables etc, 

  2. Your Business Logic Methods may be pruned based on changes, to reduce SQL.

    • For example, an expression may refer to parent data. If the other attributes in the expression are not changed, the SQL to retrieve the parent is averted.


Forward Chaining
Derivation logic may (or may not) result in additional attribute changes, thus requiring execution of logic that depends on them.  Such change propagation (like a spreadsheet) is called Forward Chaining.

Since some rules (SumsCountsParent References) are inherently multi-object, such Forward Chaining can trigger the access and logic execution of related objects.  In particular:
    1. Adjust: Changing child data (e.g., an Order Amount) will adjust the parent data (e.g., a Customers Balance). This will invoke the Customer logic processing, which can itself further forward chain.

    2. Cascade: Changing parent data referenced by Child objects will cascade to these objects, and execute their logic

For example, imagine we have the following rules:
  1. Customer Constraint: balance < creditLimit

  2. Customer.balance = sum (purchaseorders.amountTotal)

  3. Purchaseorder.amountTotal = sum (lineitems.amount)

  4. Lineitem.amount = partPrice * qtyOrdered

Now, consider a change to the qtyOrdered; it will execute as follows:
      • recompute Lineitem.amount (since it references qtyOrdered)

      • this causes the Logic Engine to:
        • access the Purchaseorder (quite likely from cache)
        • adjust its amountTotal
        • execute Purchaseorder logic (for validity, derivations... and more forward chaining)

      • the amountTotal change causes the Logic Engine to
        • access the Customer
        • adjust the balance
        • execute Customer logic (e.g., check the Credit Limit)

Constraint/Action logic
Forward Chaining is performed first within the altered object; that is, all of its (non-pruned) Formula derivations are executed prior to Adjustment / Cascade chaining.  When the local formulas are complete, the system next executes any relevant Action and Constraint logic.  

As the last step Business Logic Component instance logic execution, the system performs Adjustment / Cascade Forward Chaining.  This reduces the number of component instantiations.

So, in the example above, there are 3 nest levels:
  1. The originally updated Lineitem
  2. The Adjustment update to Purchaseorder
  3. The Adjustment update to Customer
Your Business Logic methods can access the (component) nest level via logicContext.getLogicNestLevel().  This is most commonly used to distinguish client updates vs. Forward Chained updates.

Optimizations

Since SQL actions incur substantial overhead, the Business Logic Optimizer minimizes/eliminates SQLs with advanced techniques for pruning and adjustment.

The Optimizer governs Logic Execution to implement the Performance optimizations described above.  In particular, it governs the Forward Chained rule execution, pruning rules whose referenced attributes are not altered, utilizing:
  • references are cached by the Dependency Analyzer

  • change information gathered by the Transaction Analyzer
Another key optimization is the technique for adjustment.  This utilizes Transaction Analysis information to compute the delta by which the sum/count is adjusted (again, using Transaction Analysis), so that such updates require only a single SQL update, rather than an expensive aggregate query.


Commit Phase

After all the submitted rows are processed in the Logic Phase, Commit logic is executed - logic specifications enable you to designate logic that is only applicable after all the submitted rows are processed. This enables an Order, for example, to determine whether it includes any Lineitems.

Architectural Positioning: Active Domain Objects

We can now summarize how Automated Business Logic results in Active Domain Objects that fit in to your overall architecture:

Declarative Business Logic for Hibernate/JPA Domain Objects,

Encapsulated by Event-based Injection,

Automate rich multi-table business logic,

As Components that plug in to your Architecture


The underlying concepts are further developed in the sub-sections below.

Declarative

Declarative means the system assumes responsibility for

Encapsulation: automated re-use, with existing Hibernate APIs

Encapsulation of logic means that any Hibernate updates invoke the relevant logic, automatically.  While encapsulation is traditionally implemented by Object technology, we achieve the same re-use of logic that automates the compliance of your data with your business requirements.

Note that re-use is automatic.  This is in sharp contrast to traditional approaches, which achieve re-use only by careful object design.

Moreover, this is achieved using the existing Hibernate APIs, so changes are not required to existing code. This is particularly important in utilizing Automated Business Logic with frameworks that automate Hibernate access.

Event-based Injection: Domain Objects unchanged

While injection is usually associated with compile-time injection of logic, we achieve the same effect by plugging into Hibernate events.  This means your Domain Objects are not altered, either at compile time or at runtime.

Multi-table business logic - subsumes the bulk of the service layer

There is a fair bit of confusion in the industry regarding where to put business logic: in the domain objects, or in a service layer.  Rich, multi-table logic does not fit neatly into any obvious existing object.

Domain layer proponents regard Anemic Data Objects to be an anti-pattern, since they don't encapsulate business logic.  Service layer proponents express concerns about how to embed multi-table logic - is the adjustment of a Customers' balance a responsibility of the Purchase Order, the Customer, or some mediator object?

Automated Business Logic not only provides automation, it can dramatically simplify this quandary.  Multi-table logic is automated - from the "what" based design objective - including its implementation design.  That is what we mean by Active Domain Objects - domain objects that actively enforce your logic by acting in concert with related objects on the basis of declarative rules.

So, for the vast majority of your transactions, Active Domain Objects address the multi-table requirements (and complexity) traditionally associated with a Service Layer.  You may still elect to provide a Service Layer, for example to publicize APIs, or to address transactions that are not fully REST-based.  But even in these cases, you will find that the Service Layer will be thin: it needs only to choreograph updates to the Domain Layer, since it can be assured these will enforce the multi-table business logic.

Pluggable Logic Components

While this approach can have a dramatic impact on the time, cost and complexity of building applications, it requires no changes to your existing approaches:
  • Architecture and Framework - since Business Logic Components plug in to Hibernate events, there is no impact to how your code - or your framework - invokes Hibernate.

  • You continue to utilize a familiar language - Java or Groovy

  • You continue to utilize your IDE Tools such as Code Editors and Debuggers
So, you continue to develop applications in the same manner as you always have, fully preserving your teams investment in learning and technology.  You simply augment your existing process to increase the data model definitions you have always provided, resulting in a dramatic decrease in coding and maintaining transaction business logic.

We characterize this enhanced model definition as Active Domain Objects.  That is, instead of Anemic Domain Objects that provide structure and (raw) read / write, Active Domain Objects encapsulate the logic for multi-table constraints, derivations and actions.

Logicdoc is an extension of Java/Groovydoc that generates documentation for your Business Logic Components and your Domain Objects, including graphical UML diagrams. You can also capture your systems Use Cases and Requirements, with Requirements Traceability to the implementing logic.

Logicdoc is entirely optional, and can be used at any stage:
  • during analysis to document requirements
  • at design time to document the logic intention
  • at maintenance time to recapture design intent 
Logicdoc enables the collaboration between IT and Business Users, which can reduce Requirements Risk.
You can use the console to monitor the execution of a multi-user system, including rule execution / performance statistics, transactions, objects, etc.