Click here to Skip to main content
15,885,757 members
Articles / Web Development / ASP.NET
Article

Using a Rules Engine to Separate Business Rules from the Application

Rate me:
Please Sign up or sign in to vote.
3.46/5 (15 votes)
29 Aug 2006CPOL3 min read 120.1K   2K   68   20
Using a Rules Engine to separate Business rules from the application
Demo Application

Introduction

I believe that a de-coupling of business logic from an application makes for more robust and maintainable code. If done correctly, the logic for the application can be maintained by "non-programming" people using their terminology and methodology. The application can continue to use the business logic as it changes over time and under changing circumstances because the application does not contain the business logic, but only uses it. The location of the business logic can then be centrally located and maintained, or customized and sent for specific reasons for specific installations.

This demo Visual Studio solution is a very simple example (kind of like using an elephant gun to kill a flea). I prepared this example to be easily understood, but have enough complexity to illustrate the capability.

The Rule Engine is freely available for all uses here (CodeProject article).

An on-line example can be seen here.

In order to use a Rules Engine for any automation, I found some requirements:

  1. The Rules must be easy to make
  2. The Rule maker must be able to easily define how rules affect each other.
  3. The Rules Engine must be deterministic. There can be one and only one answer to any question.
  4. The Rules Engine must provide event listening so that the application can react to changes in the Rules Engine
  5. The Rules Engine must be able to respond to changes from the application.

The Rules Engine used in the demo meets these requirements.

Background

This demo is a simple invoice creation system. This invoice is for calculating varying quantities of widgets. In addition to calculating prices, the business rules must apply quantity discounts, which are different for each widget type. The weight of the total widgets is calculated and an appropriate shipper is selected and each shipper has a different rate. There is a discount applied for each customer based on the current balance that s/he has with the Widget Barn.

The order for calculating the invoice is:

  1. Multiply the quantity of each Widget to get the total price
  2. Multiply the quantity of each Widget to get the total weight
  3. Compute the discount for the quantities of each widget (each widget has its own quantity discount rate)
  4. Subtract the discount for each widget from its total price
  5. Sum the total prices of the widgets minus the discount amounts
  6. Compute the customer discount based on the balance owed and subtract it
  7. Sum the total weights of the widgets ordered
  8. Determine which shipper to use based on the total weight
  9. Calculate the shipping costs
  10. Sum the total price minus discounts to make the invoice total

Using the Code

The demo uses an XML file for defining rules. The rules engine does not require it but it is available and it works good. The XML rules file can be loaded from a Web site or from a local file. Both are provided for the demo application. The first thing is to open a rules file:

C#
private void menuRulesWebFile_Click(object sender, EventArgs e)
{
    JaxlabReader reader = new JaxlabReader(homeWebfile, "");
    rulesEngine = reader.NewRulesEngine;

    cbxCustomers.SelectedIndex = 0;
}

Once loaded, the Rules engine will execute upon changes defined in any triggers, so all I have to do is change values to initiate the changes.

C#
rulesEngine.SetVariableValue("SilverQty", qtySilver.Text);

Or I can "run" rules at any time like this:

C#
rulesEngine.RunRule("CalculateInvoice");

I simply set up a method to get the values I need for the invoice and write it to the textbox. The Rules Engine does everything in between.

C#
private void MakeInvoice()
{
    List<STRING> lines = new List<STRING>();
    lines.Add("Invoice Demo");
    lines.Add("");

    lines.Add("Customer Name: " + rulesEngine.GetVariable("CustomerType").StringValue);
    lines.Add("Customer Balance: " + 
              rulesEngine.GetVariable("CustomerBalance").DoubleValue.ToString("c"));

    lines.Add("");
    lines.Add("Quantity of Gold Widgets: " + 
              rulesEngine.GetVariable("GoldQty").StringValue + 
              "\t Price: " + rulesEngine.GetVariable
              ("GoldPrice").DoubleValue.ToString("c") + 
              "\t Total Price: " + rulesEngine.GetVariable
              ("GoldTotalPrice").DoubleValue.ToString("c") + 
              "\t Discount: " + rulesEngine.GetVariable("GoldQtyDiscount").StringValue);

    lines.Add("Quantity of Silver Widgets: " + 
              rulesEngine.GetVariable("SilverQty").StringValue +
              "\t Price: " + rulesEngine.GetVariable
              ("SilverPrice").DoubleValue.ToString("c") +
              "\t Total Price: " + rulesEngine.GetVariable
              ("SilverTotalPrice").DoubleValue.ToString("c") +
              "\t Discount: " + rulesEngine.GetVariable
              ("SilverQtyDiscount").StringValue);

    lines.Add("Quantity of Star Widgets: " + 
              rulesEngine.GetVariable("StarQty").StringValue +
              "\t Price: " + rulesEngine.GetVariable=
              ("StarPrice").DoubleValue.ToString("c") +
              "\t Total Price: " + rulesEngine.GetVariable
              ("StarTotalPrice").DoubleValue.ToString("c") +
              "\t Discount: " + rulesEngine.GetVariable("StarQtyDiscount").StringValue);

    lines.Add("");
    lines.Add("\t\t\t\t\t\t         Subtotal: \t" + 
              rulesEngine.GetVariable("PriceSubTotal").DoubleValue.ToString("c"));
    lines.Add("\t\t\t\t\t\t         Discount: \t" + 
              rulesEngine.GetVariable
                  ("CustomerTypeDiscountAmount").DoubleValue.ToString("c"));
    lines.Add("\t\t\t\t\t\t         Shipping: \t" + 
              rulesEngine.GetVariable("ShippingTotal").DoubleValue.ToString("c"));
    lines.Add("\t\t\t\t\t\t              Tax: \t" + 
              rulesEngine.GetVariable("SalesTaxTotal").DoubleValue.ToString("c"));
    lines.Add("\t\t\t\t\t\t          Shipper: \t" + 
              rulesEngine.GetVariable("ShipperName").StringValue);

    lines.Add("");
    lines.Add("");
    lines.Add("\t\t\t\t\t\t            Total: \t" + 
              rulesEngine.GetVariable("InvoiceTotal").DoubleValue.ToString("c"));

    txtBox.Lines = lines.ToArray();
}

Points of Interest

The event handling with the Rules engine made this easy to implement.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Website: http://www.somedeveloper.us

Comments and Discussions

 
QuestionUse XML Pin
Artem7218-Aug-11 5:32
Artem7218-Aug-11 5:32 
GeneralMy vote of 2 Pin
Kikoz689-Jul-11 4:43
Kikoz689-Jul-11 4:43 
GeneralGUI for making rules Pin
Banty Nigam12-Feb-09 20:23
Banty Nigam12-Feb-09 20:23 
GeneralRe: GUI for making rules Pin
Jark3-Sep-09 20:39
Jark3-Sep-09 20:39 
QuestionRuleEngine1 Pin
LearnTech28-Dec-07 22:29
LearnTech28-Dec-07 22:29 
AnswerRe: RuleEngine1 Pin
ahagel2-Jun-09 9:46
professionalahagel2-Jun-09 9:46 
Questionneeds Help on rule engine Pin
LearnTech28-Dec-07 17:31
LearnTech28-Dec-07 17:31 
QuestionWWF (windows workflow framework) ? Pin
gogac10-Aug-06 7:18
gogac10-Aug-06 7:18 
are there any advantages over WWF (which supports workflows, state machines, rules, XOML, BPEL) ?
AnswerRe: WWF (windows workflow framework) ? Pin
Jeff Bramlett10-Aug-06 7:42
Jeff Bramlett10-Aug-06 7:42 
AnswerRe: WWF (windows workflow framework) ? Pin
Grav-Vt20-Aug-06 10:21
Grav-Vt20-Aug-06 10:21 
GeneralRe: WWF (windows workflow framework) ? Pin
gogac20-Aug-06 12:44
gogac20-Aug-06 12:44 
AnswerRe: WWF (windows workflow framework) ? Pin
Danilo Mendez5-Apr-07 15:45
Danilo Mendez5-Apr-07 15:45 
GeneralExamples Pin
Dustin Metzgar10-Aug-06 6:11
Dustin Metzgar10-Aug-06 6:11 
GeneralRe: Examples Pin
Jeff Bramlett10-Aug-06 6:49
Jeff Bramlett10-Aug-06 6:49 
GeneralRe: Examples Pin
Dustin Metzgar10-Aug-06 7:12
Dustin Metzgar10-Aug-06 7:12 
GeneralRe: Examples Pin
Jeff Bramlett10-Aug-06 7:34
Jeff Bramlett10-Aug-06 7:34 
GeneralWhere's the source Pin
Juergen Posny10-Aug-06 5:09
Juergen Posny10-Aug-06 5:09 
GeneralRe: Where's the source Pin
Jeff Bramlett10-Aug-06 6:08
Jeff Bramlett10-Aug-06 6:08 
GeneralRe: Where's the source Pin
Jeff Bramlett29-Aug-06 9:10
Jeff Bramlett29-Aug-06 9:10 
GeneralRe: Where's the source Pin
sairashid11-Apr-10 14:16
sairashid11-Apr-10 14:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.