Complex Example Using Reactive
A bill of materials is a well-known complex use case for database applications. A bill of materials is the list of the raw materials, sub-assemblies, intermediate assemblies, sub-components, parts and the quantities of each needed to manufacture an end product. In the example, products (“kits”) are recursively composed of other products (“components”), where a product can be a component in more than one kit. This is modeled with the structure shown here: Consider when a component product price is changed and that component is part of a kit or multiple kits. Each of the kits must reflect the price change. For example, if a bolt increases in price, so do all the kits that use bolts: wings, fuselages, etc. And of course it is recursive. This pricing function is defined in Reactive Programming with the four expressions below: These react to a change in Products.price as follows:- product_billofmaterials.value reacts to the row.product.price change. So, the price change is propagated into each of the related product_billofmaterials rows
- for each such row, the Products.sum_components_value reacts to the value change and is recomputed (reactive chaining)
- the adjustment in Products.sum_components_value chains to adjust the Products.price - which recurses over the same logic for this Product (reactive chaining across tables)
- Observe the ternary if/else expression (<if-exp> ? <if-value> : <else-value>), based on the count_components. Counts are commonly used for logic that depends on whether there is related data, here selecting the computed price (for a kit) or the entered price.
Procedural Integration
Reactive Programming is an excellent approach for computational dependencies, typically initiated by database update events. So Reactive is quite a good match for REST PUT, POST and DELETE events. But what about logic that involves external systems or logic that is not exactly an update (PUT)? A powerful and familiar approach is event handlers in procedural languages, such as JavaScript, Java, and C#. Such integration requires an object model (sometimes called “active records”) as a basis for the events. In the context of database logic, the Reactive Programming environment creates the object model automatically from the schema, consisting of:- Objects for each table (Orders, Customers, etc.)
- Accessors for columns and related data, including database access with appropriate caching
- Persistence services (find, insert, update, delete verbs)
- Row Insert/Update/Delete events
- Send Mail
- Deep Copy
Send Mail
Email is external to the database schema, so there is no <table.column> on which to define a Reactive expression. But it’s straightforward to define an event on the Orders object:if (row.amountTotal > 1000) {
log.debug("sending email for: " + row);
sendEmail (row.salesRep.manager.email,
“Congratulations due”,
“Congratulate “ + row.salesRep.name + “!”)
} So then:- row represents an Order object,
- row.amountTotal is an attribute,
- row.salesRep.manager.email is the email address of the order’s salesmen’s manager (using accessors for related data)
- sendEmail is an externally supplied procedural function in JavaScript
Deep Copy
Events also provide a basis for re-usable solutions for complex logic. Consider performing a clone or deep copy of an Order. It’s not exactly an update, but needs to trigger the deep copy logic. Here is an event defined on the Orders object:if (req.urlParameters.get("clone") !== null) {
log.debug("cloning: " + row);
var deepCopy = ["lineitems"];
SysLogic.copyThisTo("orders", logicContext, deepCopy);
} This event is defined in the context of a RESTful server, so the req object provides access to REST parameters (req.urlParameters), supplied like this on the REST call:http://xxx/rest/yyy/orders?clone=trueThe copyThisTo is the deep copy service, parameterized with the target object (“orders”) and the set of deeply-copied objects (“lineitems”). This example illustrates adding rule types (here deep copy), in addition to logic triggered without an update of the cloned order. Integration with a procedural language means you can build a set of services that automate patterns such as deep copy. Such integration means that all operations are subjected to Reactive logic, therefore naturally adjust dependent data.