Business Rules sample (desktop) (Delphi)

The Business Rules sample demonstrates how Business Rules Scripting can be used to validate changes made by client applications at the server level, before committing the changes to the back-end database. The benefit of putting change validation in the middle tier is that rules can be changed and updated on the fly without having to deploy new versions of the client applications and deal with the possibility that some users have not yet updated to the latest client version.

Getting Started

The sample is located in C:\Users\Public\Documents\RemObjects Samples\Data Abstract for Delphi\BusinessRules.

To build it, you will need a Delphi RAD Studio and like all the samples provided with Data Abstract for Delphi you will need to be running Relativity Server with the DASamples Domain and the Simple Schema available.

Running the Sample

The sample provides a simple UI that allows you to try out making changes to the table content; both ones which fit the defined business rules, but also those that violate the rules. There is no data validation in the sample itself, so any changes to the local table data will be successful. It is only when you attempt to apply the changes to the server that validation will occur.

When run the sample sets up a connection to an instance of Relativity Server and retrieves the data from the "ClientsWithScripting" table from the "Simple" schema and displays it in the table.

The UI is split into two parts, the first is a toolbar that contains a combo box that contains the server address, and three buttons. The "Load" button will retrieve the table data freshly from the server. The "Delete" button will delete the currently selected row, and finally the "Apply" button will attempt to apply the local changes to the server.

At the bottom of the window there 3 suggested rules for you to try and violate. If for instance you try and remove a row, you will see that it will properly disappear from the local table data. However when you press the "Apply" button, an exception will be raised by the server and an error dialog displayed.

Examining the Code

The sample is comprised of two classes; the ClientDataModule handles all interaction with the schema and is the focus of the code below. The ClientForm handles the main user interface.

As the focus of this sample is using Business Rules Scripting for data validation there is no discussion of the code in those classes.

The Business Rules

The Business Rules are located on the server in a schema and allow you to add business logic that can be used to validate data or enforce access restrictions. The rules are written as small blocks of EcmaScript, otherwise known as JavaScript, that are triggered on certain events for instance on processing an error, after applying a delta change, before getting data and so on. To learn more about business rules, how to add/edit them and the events available check out the articles Business Rules Scripting, Business Rules Scripting API and Business Rules Scripting Events.

Here we are interested in the rules that are assigned to the "ClientsWithScripting" table. If you open Relativity Server Admin Tool and connect to the instance of Relativity Server your using. Navigate to the "DASamples" domain and select the "Simple" schema and press the "Open in Schema Modeler" button.

In Schema Modeler navigate to the "ClientsWithScripting" table, expand the tree and click on "Scripts" node. This will display a page which contains the business rules used in this sample (duplicated below) which you can modify to test if you desire.

The beforeProcessDeltaChange event is trigged each time a change is received from a client application, but before the change has been processed. It takes four arguments that are:

  • a Delta object that holds all of the changes.
  • a Delta Change object that holds information about a single delta change and contains the old and new values
  • wasRefreshed a bool this indicates if the record was refreshed
  • canRemove a bool to indicate if the record is currently allowed to be removed

There are 5 validation rules to cover all of the conditions used in this sample. If none of them generate a fail message, then the function returns and the changes are applied to the table. The fail message will not be returned until all of the delta changes have been processed.

// From the Schema
// Called before each change is applied to the database
function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
    var msg = "";
    var client = "ClientID=" + change.newValues['Id'];


    // Delete rule
    if (change.isDelete) {
        client = "ClientID=" + change.oldValues['Id'];
        msg = "Business Rule: #CLIENT: Deleting clients is prohibited!"
        .replace("#CLIENT", client);
        fail(msg);
    }

    // Discount rule. Min check
    var newDiscountValue = Math.round(100*change.newValues['Discount']);
    if (newDiscountValue < 0) {
        msg = "Business Rule: #CLIENT: Discount cannot be less than zero! Current value is: #VAL %"
        .replace("#CLIENT", client)
        .replace("#VAL", newDiscountValue);
        fail(msg);
    }

    // Discount rule. Max check
    if (newDiscountValue > 60) {
        msg = "Business Rule: #CLIENT: Discount cannot be greater than 60%! Current value is: #VAL %"
        .replace("#CLIENT", client)
        .replace("#VAL", newDiscountValue);
        fail(msg);
    }

    // Birthdate rule. Min check
    var minDate = new Date(1940, 1, 1, 0, 0, 0, 0);
    var newBirthdateValue = change.newValues['Birthdate'];
    if (minDate > newBirthdateValue) {
        msg = "Business Rule: #CLIENT: Birthdate cannot be less than 1940! Current value is: #VAL"
        .replace("#CLIENT", client)
        .replace("#VAL", newBirthdateValue);
        fail(msg);
    }

    // Birthdate rule. Max check
    var maxDate = new Date();
    if (maxDate < newBirthdateValue) {
        msg = "Business Rule: #CLIENT: Birthdate cannot be in future! Current value is: #VAL"
        .replace("#CLIENT", client)
        .replace("#VAL", newBirthdateValue);
        fail(msg);
    }