5

I am writing a rules/eligibility Module. I have 2 sets of data, one is the customer data and the other is the customer products data. Customer data to Customer products data is one to many.

Now I have to go through a set of Eligibility rules for each of this Customer product data. For each customer products data, I can say the customer is eligible for that product or decline the eligibility and should move on to the next product record.

So in all the rules, I need to have access to customer and customer product data(the particular record that the rules are being executed against). Since all the rules can either approve a product or decline a product, I created an interface with those 2 methods and is implementing the this interface for all the rules. I am passing the Customer data and one product data for all the rules (because rules should be executed on each row of customer product data). An Ideal situation would be having the customer and customer product data available for the rule instead of passing them to each rule.

What is the best way of doing this in-terms of architecture?

Edit: Here is what I am doing

public class CustomerContract
{
    public DateTime StartDate { get; set; }
    public DateTime endDate { get; set; }
    //Other Contract related details

}
public class Customer
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string state { get; set; }
}

public class CustomerInfo
{
    public CustomerInfo(int CustomerID)
    {
        Customer = new Customer();// Get Customer from DB
        AppliedProducts = new List<CustomerProduct>();// Get the customer products by customerID
        CurrentContract = new CustomerContract();//Get the contract by CustomerID, State
        declineReasons = new List<int>();// just intailizing. the decline codes are added by rules.
    }

    public CustomerContract CurrentContract { get; set; }
    public IList<CustomerProduct> AppliedProducts { get; set; }
    public IList<int> declineReasons { get; set; }
    public Customer Customer { get; set; }
}

public class CustomerProduct
{
    public decimal AmountCharged { get; set; }
    public int DeclineReasonID { get; set; }
    public int Product { get; set; }
    public decimal DiscountApplicable { get; set; }
    public decimal AmountQuoted { get; set; }
    public decimal Tax { get; set; }
}

public interface IRule
{
    // Since we need to have access to contracts when deciding the eligibility
    CustomerProduct ExecuteRule(CustomerProduct currentproduct, CustomerInfo customerInfo);

    //when denied, 
    CustomerProduct DenyProduct(CustomerProduct currentProduct);
}

public class EligibityEngine
{
    List<IRule>  rules = new List<IRule>();
    private CustomerInfo c;

    public EligibityEngine(int CustomerID)
    {
        c = new CustomerInfo(CustomerID);
        LoadRules();
        foreach (var customerProduct in c.AppliedProducts)
        {
            ExecuteRules(customerProduct);
        }
    }

    private void ExecuteRules(CustomerProduct currentProductItem)
    {
        foreach (var rule in rules)
        {
            currentProductItem = rule.ExecuteRule(currentProductItem, c);
        }
    }

    private void LoadRules()
    {
        // add all the IRule types here
        //rules.Add();
    }
}

When I execute the rule, I have pass the customer Product data and customer data. I have to pass the result of one rule execution to the other. In the above pattern, if the rule changes data in customerInfo class, it is not save unless it is passes as ref.

My question is I want the all rules to have access to this data with out passing them in. So I need not worry about capturing the out put of the rule.

I want the executeRule method to void.

1
  • What do mean by "available to the rule" vs. "passing them to each rule"? Also, could you include in your question the interface you created? Nov 25, 2011 at 11:57

3 Answers 3

3

The Specification pattern seems to suit your needs pretty well. The sample code given is precisely about Customers and Products.

0
0

I'm not sure whether this answer will help but I give it a shot anyways.

Based on my understanding of what you are saying, here is my suggestion:

You already have a module that provides access to the customer data and you have module that provides access to the customer product data. For each of these modules define one (or more) interfaces to access the data that will be used by at least one rule.

Then create an 'IRuleContext' interface that has methods to obtain the interfaces for accessing customer data and customer product data.

Write a module "Rules" such that each rule takes a parameter of type 'IRuleContext'. For example you could have an abstract base class 'EligibilityRules' with an abstract method of 'bool AppliesTo(IRuleContext)'. Then you could implement a number concrete rules with each of them using a different algorithm to determine eligibility.

Instead of modules you could also have services, components, classes, etc. This is dependent on what technology you are using. Also make sure that your choices meet your requirements, for example a web service may not an option in your scenario.

0

As @ian31 pointed out, you need the Specification pattern. I once had a very similar need and the design I came up with is roughly as follows:

  • Each business rule (eligibility rules, in your case) was implemented as a logical predicate, or specification.
  • As in your case, there were chains of rules, that could be arranged in a conjunction (AND operation) or in a disjunction (OR operation). In a disjunction, the parameter had to be accepted by at least one of the rules and that rule should be reported by the rule engine. In a conjunction, the parameter should be accepted by all the rules, in which case the first rule in the chain should be reported.
  • There was an interface Rule that had three important implementations: one that delegated to a predicate, and the other two implemented the conjunction and disjunction requirements described above.
  • There was a RuleFactory that arranged the rules in the order specified by the customer (what is being done by the LoadRules() method in your example). This order could be easily changed without affecting any other part of the application.

It's important to note that none of these classes changed any state. They were pure functions that either accepted or rejected data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.