Can Reactive Programming Handle Complexity?

A previous article in this series illustrated the expressive power of Reactive Programming, where five lines of Reactive could solve a problem that required 500 lines using Java or 200 lines using triggers. And understandably, many questioned whether Reactive enables you to address not just typical problems, but complex ones as well.

Certainly it can’t solve all use cases, but Reactive Programming is very capable of addressing many complex problems, and can address all other scenarios via a transparent integration with procedural languages.

So in this article, we wanted to explore how Reactive can handle complexity using two different scenarios:

Complex Example Using Reactive: How core Reactive Programming deals with a classically complicated database application (e.g. a bill of materials price rollup). 

Procedural Integration: How Reactive Programming can be combined with procedural languages to address external systems (e.g. email) and transactions not initiated by a database update (e.g. a Deep Copy).

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.

This is a non-trivial problem. It illustrates that while reactive does not eliminate the thinking, it can solve this very complex case elegantly with few expressions.

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

Consider the following examples:

  • 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

Similar services might be defined for sending guaranteed delivery messages, starting workflow processes, accessing related systems and so on.

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=true

The 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.

Conclusion

Certainly, the expressive power of Reactive Programming can make software development more efficient. For it to be truly valuable however, it must address not only typical cases, but any kind of complex use cases as well.

This article has illustrated how Reactive addresses complex use cases like bill of materials and can address any other complex problems by combining Reactive with procedural languages. For more detail, you can explore full descriptions of these examples and additional ones here.

 

Val Huber is CTO of Espresso Logic.

Post a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>