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
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.
It is straightforward to integrate Business Logic with your Services Oriented Architecture, either for message processing or message invocation.
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
Order, and so forth.
Your data is represented as Java
In addition to the POJOs, you define mappings and semantics that
enable Hibernate to enforce proper semantics. These can take the form
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.
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
- Configure Hibernate to invoke the Business Logic engine, and
- 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:
- Your logic is typically expressed in the annotation; more complex logic can be expressed in the method
- 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).
- 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
- 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
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 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:
- 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
- Children Reference: objects on the "one" side can aggregate their child data, such as
Customer.Balance = Sum(orders where order.isReady)
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
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
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:
- automatic type conversions dramatically simplify expressions. Most of your logic is simply expressions...
lineitem.price * lineitem.quantity
lineitem.getPrice().multiply( new BigDecimal(lineitem.getQuantity()) )
- syntax simplification makes your logic easier to read...
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:
- Code Completion - for example, pressing Control+Space may provide a list of choices (methods, variables etc)
- Syntax highlighting
- Error indication
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.
The Logic Engine is designed to minimize the cost and number of SQL operations. For example:
- Parent Reference Pruning: SQLs to access parent rows are averted if other other (local) expression values are unchanged.
- 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.
- Adjustment: for persisted sum/count aggregates, the system does not issue
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
- 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.
Most Business Logic specifications are simple spreadsheet-like expressions:
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
- 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
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
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.
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
Once the component is discovered, the Logic Analyzer then discovers your Business Logic Methods, identified by annotations. These are located using Java introspection.
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.
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
Instance Variable Initialization
illustrated above (e.g.,
PaymentPurchaseorderAllocation). These system-created proxy objects enable your logic to:
- Reference current/old Domain Object values
- Access related data (via Domain Object accessors, such as
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.
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
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:
- You can use your IDE's debugger to set breakpoints, review variables etc,
- 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.
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.
- 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.
- 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:
- Customer Constraint: balance < creditLimit
- Customer.balance = sum (purchaseorders.amountTotal)
- Purchaseorder.amountTotal = sum (lineitems.amount)
- Lineitem.amount = partPrice * qtyOrdered
Now, consider a change to the
qtyOrdered; it will execute as follows:
- recompute Lineitem.amount (since it references
- this causes the Logic Engine to:
- access the
Purchaseorder (quite likely from cache)
- adjust its
Purchaseorder logic (for validity, derivations... and more forward chaining)
amountTotal change causes the Logic Engine to
- access the
- adjust the
- execute Customer logic (e.g., check the Credit Limit)
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:
- The originally updated Lineitem
- The Adjustment update to Purchaseorder
- The Adjustment update to Customer
Your Business Logic methods can access the (component) nest level via
.getLogicNestLevel(). This is most commonly used to distinguish client updates vs. Forward Chained updates.
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.
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 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 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.