Saturday, 27 November 2010

Madura Rules Part 3

This has been a while coming. Part 1 covered the types of rules available and part 2 was about how to configure Madura Rules using Spring.

In this entry I want to cover some of the less obvious features in Madura Rules: decision tables and constants. These are closely related to choice lists in Madura Objects.

Decision Tables

Decision Tables are descendants of a construct in COBOL. Oh, yes, COBOL really did have some smart stuff in it. Actually these are somewhat simplified from the COBOL decision tables but they do a good job.

Start with an XML file that looks like this:

<DecisionTable name="business-customerType" scope="Customer" message="nz.co.senanque.newrules.decisiontable.business-customerType">
  <ColumnNames>
      <ColumnName autoAssign="true">business</ColumnName>
     <ColumnName>customerType</ColumnName>
  </ColumnNames>
  <Rows>
      <Row>
        <Column>AG</Column><Column>a</Column>
      </Row>
      <Row>
        <Column>AG</Column><Column>f</Column>
      </Row>
      <Row>
        <Column>FISH</Column><Column>b</Column>
      </Row>
      <Row>
        <Column>FINANCE</Column><Column>c</Column>
      </Row>
      <Row>
        <Column>FINANCE</Column><Column>d</Column>
      </Row>
      <Row>
        <Column>FINANCE</Column><Column>e</Column>
      </Row>
      <Row>
        <Column>FINANCE</Column><Column>f</Column>
      </Row>
  </Rows>
</DecisionTable>

As you can see this is mostly rows and columns with values in each cell. COBOL's equivalent allows expressions in each cell.

The decision table has a name, a relevant object (Customer in this case) which is the equivalent of the scope in the other rules. There is also a message identifier which is delivered as an error if an attempt to set an incorrect value is made.
The column names refer to fields in the Customer object.

Below that are rows and columns. Each row specifies a valid combination. So if we set the business to B then the only valid values for business are AG and FISH. If your application examines the metadata for business, say to create a drop down list, then it will give only those values.

If we set the customerType to A then there is only one valid value for business and, because we set the autoAssign attribute in the columnName, then that value will actually be set.
We can, of course, decide to set the business value first and have it decide what options are available for customerType instead. And we can have more than two columns.

Your decision table data comes from the XML file by default, but you can write a factory in Java to deliver the data.

Constants
You can specify soft constants in your rules like this:

rule: Customer "Determine business from customerType"
{
  if (customerType == ${xyz})
  {
    business = IndustryType.AG;
  }
}

Here xyz is a constant, except that we might want to change that constant sometimes so we can specify it this way. Then we write some XML that looks like this:

<Constants>
  <Constant name="xyz">aaaab</Constant>
</Constants>

Like the decision table the value may come from the XML or it may be overridden by a factory you can supply yourself which delivers the value. This is useful for values that cannot be determined before deployment time, and/or for values that are used in multiple places and you want to ensure they are actually all the same value.

Injecting the XML

All of the decision tables and all of the constants can reside in a single XML document. In fact this document can also contain the choice lists used by Madura Objects. But they can also live in separate documents, injected separately into the rules engine. This allows options such as generating the decision tables by some external program etc. All documents are injected as Spring resources into the engine so you have all the deployment options supported by that (classpath, file, URL etc).

There is still more to cover, such as how to add external functions to the rules and how to handle I18n issues. But that's another post.
Post a Comment