Version 3

    I have observed a number of fundamentally different approaches for implementing business logic. Some advocate a services layer, others advocate adding logic to domain objects. I have not yet read anything entirely convincing.  Though the term is often used, there is considerable confusion, ranging from definition to architecture to proper tool utilization.


    Having considered about this for a while now, I have collected some thoughts in this paper.  It’s an approach that accepts a Service Layer as an anti-pattern, and proposes event injection of declarative logic into Domain Objects - a Rich Active Declarative Data Model.

     

    While the concept can be applied to multiple architectures, our company has implemented what we believe is a preferred implementation, namely:

    1. Specify business logic in "logic classes" that parallel your domain objects.  Logic includes
      1. Derivation rules, in particular multi-table derivation rules such as Customer.balance = sum(order.amount where paid = false).  Note that:
        1. Derivations are multi-table, as in the example above
        2. Derivations can chain, eg, Order.total = sum(Lineitem.amount) and Lineitem.amount = quantity * Part.price
        3. Chaining and dependencies are automatically handled
      2. Constraint Rules - in particular multi-attribute constraints, such as balance < creditLimit
      3. Action Rules - address requirements such as auditing, or invoking a business rules engine such as Drools or BPM
    2. A Logic Engine plugs into Hibernate events.  At commit time (no code generation), it locates Logic Classes (if any) for each update, and executes the logic in a correct and efficient order...
      1. Correct means that the logic dependencies are determined (using byte code analysis), so changes to dependent data are propagated in a correct order including across tables
      2. Efficient means such propagation is pruned where dependent data is not altered.  This is particularly important for multi-table logic such as sums/counts, to avoid unnecessary data access

     

    This architecture has a number of advantages

      1. Excellent Architectural Separation
        1. Domain objects are unaffected
        2. Existing Hibernate APIs are unchanged.  In particular, automation is retained for frameworks that automate Hibernate calls. This has been verified with JSP, Spring, Grails, ZK, Vaadin, and GWT.
      2. It provides automated enforcement, so the logic is active
      3. Logic is encapsulated with Domain objects, so re-use is automated over all related Use Cases.  So, the rules below, while perhaps conceived for add order, are automatically applied to delete order, update order etc:
        • Customer Constraint: balance < creditLimit
        • Customer.balance = sum (order.total where paid = false)
        • Order.total = sum (Lineitem.amount)
        • Lineitem.amount = quantity * partPrice
        • Lineitem.partPrice = copy(Part.price)


    A Service Layer, often motivated by the complexities of multi-table logic, is not required for most cases.  If desired (e.g., to publish APIs for SOA access), services can be thin since they invoke the logic encapsulated into the Rich Domain Objects.

     

    It complements Drools

      1. Automates active enforcement - no explicit calls to invoke the logic engine are required
      2. Transition Logic - integration with Hibernate means logic expressions can reference old values (eg, salary > old.salary)
      3. Optimized Aggregates - Hibernate integration means that the system can prune updates requring no changes (eg, changing order date does not affect customer balance), and when updates are required, the system can use adjustment logic to avoid SQL aggregate queries or reading all related objects into memory
      4. Drools remains appropriate for Business User rule management, e.g., Decision Tables, and for cases not related to persisting transactions

     

    Business advantages

    • Agility - the 5 rules noted above replace 100 lines of traditional Java code since they automate depedency management and re-use over all related Use Cases (add order, delete order, update order etc), repeated here from above for reading convenience:
      • Customer Constraint: balance < creditLimit
      • Customer.balance = sum (order.total where paid = false)
      • Order.total = sum (Lineitem.amount)
      • Lineitem.amount = quantity * partPrice
      • Lineitem.partPrice = copy(Part.price)
    • Transparence - the rules can be read by Business Users, who can identify errors/omissions to reduce requirements risk
    • TCO - optimizations/ordering are repeated on each change, reducing maintenance costs.  Compliance verification is dramatically simpler, since you need to verify just the logic, not all the calling code.
    • Architectural automation - architects can be assured the logic is re-used and optimized, with a significant simplification over manual approaches which involve many non-trivial design choices

     

    I am interested in feedback and reactions.