Search code examples
javadroolsrule-engine

Per which data object should I create my Drools/Kie session?


I have the following POJO:

public class Transaction {

    @Id
    private long id;

    private String organisationId;

    private String resposibleName;

    private DateTime transactionTime;

    private BigDecimal amount;

    ...

}

that I would like to apply my rules to. For example, a sample rule might be on its amount:

rule "SampleRule"
    when
        $transaction : Transaction( amount > 10)
    then
        $transaction.setStatus(Status.INVALID);
end

and I also want to apply some more complex rules such as:

  1. WHEN the sum of amount fields of Transactions per same organisationId within the last week (based on transactionTime) is greaten than x, THEN $transaction.setStatus(Status.INVALID);
  2. WHEN there is a past transaction with same responsibleName as the current transaction being evaluated, but different organisationId than the current one, THEN $transaction.setStatus(Status.INVALID);

When there's a query being made to a RESTful endpoint:

  1. I am creating a new KieSession
  2. Adding a single Transaction object to the session,
  3. Adding further transaction objects that might be related to the current one with a different POJO (e.g. say PastTransaction as the exact copy with the same fields with Transaction, I am adding multiple transactions made by the same organisationId with PastTransaction POJO so that I can accumulate amount field on those while being able to apply more elementary rules on my Transaction object.
  4. In the same way, I am adding all transactions that are made by the same responsibleName with ResponsibleNameTransaction POJO, and write a rule to check if any of these is not equal to the one of my single Transaction object in the KieSession.

I was wondering if there might be an easier/more optimal way to achieve what I have been doing, and optimizing per which entity I am forming my KieSessions for. I have though of writing methods to my Transaction POJO and hit the db within the rule execution (e.g. write a method getAllTransactionsByTheResponsibleName which returns a list, then check if there's an object with different organisationId). In a same way I can add related transactions as a list to KieSession then query the list within the rule, which might at least save me from using the same POJO under different names.

Update:

Elaborating further based on comments, would that be feasible to keep about a billion Transactions in working memory (a session for all transactions)? Or alternatively, I can put transactions per responsible/organisation into KieSession (session per responsible/organisation). Then how can I apply some rules solely on the fields of the latest transaction being evaluated (or even set a field on it) when there are multiple transactions in session (e.g. $transaction.setStatus(Status.INVALID); when #1 is true, $transaction being the one that is currently evaluated)?


Solution

  • It may not be possible to let your non-techies romp completely unaided. A full discussion of what you may have to add would require full knowledge of all the rules, which I don't have. However, #1 and #2 from your Q can be handled even with billions of Transactions passing through your Working Memory.

    Use case 1: Checking for a threshold per account in a time range can be done by keeping an auxiliary fact ("AccountSums") per account with n daily sums. With any new Transaction, either update the last sum, or "shift" sums adding 0 sums and set last sum, or create new AccountSums; maintain total of last n sums- The techies can write the check based on just AccountSums (or in combination with the (last) matching Transaction.

    rule "check 7 days limit"
    when
        $t: Transaction( $orgId: organisationId,
                         $transT: transactionTime,
                         amount > 0, status == Status.VALID )
        AccountSums( organisationId == $orgId,
                     lastTransactionTime == $transT,
                     runningWeeklySum > 1000000 )
    then
        modify( $t ){ setStatus( Status.INVALId ) }
    end
    
    rule "create AccountSums"
    when
        $t: Transaction( $orgId: organisationId,
                         amount > 0 )
        not AccountSums( organisationId == $orgId )
    then
        insert( new AccountSums( $orgId ) );
    end 
    

    You'll need at least one additional rule for "shifting" the sums, etc.

    Use case 2: Maintain an auxiliary fact ("NameId"), with instances containing a responsible name and an organisationId (or a single instance containing a Map.) If another Transaction arrives, create a new NameId (or Map entry), or compare for equal and not equal Id.

    History must be condensed if the volume of your data is as you indicate. It may be possible to let the boffins write simple evaluation rules, but devising rule patterns along the lines I've outlined here is, at least for condensing the meta-data, not something you can leave to the Muggles.