Building on many lessons learned from spreadsheets, functional languages and model-driven application development environments, Reactive Programming (RP) is a recent approach to creating applications. Applying RP to database transaction logic is also relatively new, enabling applications to be expressed with far fewer statements than using traditional approaches or BPM-focused application modeling.
In the RP paradigm, you can create complex applications from a series of simple declarative statements. Many of the details of implementation and work of gluing together various sorts of application constructs can be hidden because of the declarative approach. Maintenance is easier, and the applications tend to be quite adaptable because RP provides automatic dependency management and reuse.
Reactive in a Nutshell
In imperative (procedural) programming, A=B+C is an assignment statement. A is set when the assignment statement executes. Subsequent changes to B or C do not affect A, unless you specifically re-calculate A. Software developers are responsible for understanding and managing these dependencies.
Reactive programming is a declarative approach for defining logic. Variables are defined by expressions such as A=B+C, which automatically reacts to changes in B or C.
In RP, A is updated automatically when the state of the referenced variables, B and C, change. RP eliminates the analysis in code to figure out the dependency management (i.e. what changed, what depends on that and what to do next). In this way, the paradigm resembles a spreadsheet that re-computes dependent values as data is changed.
RP is currently in use mostly in the front-end, where it is an obvious good fit. Microsoft has been particularly active in this area. But until now, RP has seldom been applied to the back-end, particularly in databases environments.
Building business logic for database applications with RP is straightforward. You specify Reactive Expressions (Rx) to define the meaning of columns—how they are derived. These expressions are bound to columns, either stored or virtual. For example, the English definition of the customer balance field might be: the customers’ balance is equal to the sum of its order totals, for the orders that are not paid. From a systems view it might be the following:
Reactive processing for this Rx follows this flow:
Define: The customer balance field; you assign an Rx such as the one above to the column. Note the Rx includes references to other columns, the orders’ total and paid flag. You can also define validation expressions, such as balance < limit.
Watch: The RP environment parses the expression and identifies the column dependencies, order.total and the paid flag. It then watches for changes in these values, including inserts and deletes.
React: When values of either order total or paid change, the value of customer balance is adjusted. That, in turn, can chain to values dependent on it.
Reactive expressions look simple, but are powerful enough to model applications that address complex problems. Part of this is achieved because of the declarative approach in itself. In addition, the representational power of the declarations has been made stronger because they are based on a close study of common patterns in application logic. Here are some of the ways that complexity is reduced by RP.
Logic dependencies are handled by the environment, instead of the developer; the underlying concept is chaining—referenced data itself can be dependent upon other Rx, as in this sample use case for “place order.”
In this example, logic chains from item to order to customer. When an increase in quantity results in a customer balance that exceeds credit limit, it raises an exception and rolls back the transaction:
In imperative programming, methods and services are executed only when you call them. By contrast, RP automatically invokes Rx when data changes.
The customer.balance Rx, specified for the place order use case, states that its value is the sum of the unpaid order totals. This expression is automatically reused over all transactions that use the field. So, the balance is increased if an order is added, decreased when it is deleted or paid, increased when you raise the quantity, and so forth.
With imperative code, it is easy to overlook one of the use cases introducing ‘corner case’ design and coding errors. Reactive practically eliminates this class of error.
Automatic Expression Sequencing
Expressions are added in any order. Just like in a spreadsheet, the dependency manager optimizes and sequences the expressions into the order of operations that yields correct results.
This simplifies maintenance. Instead of code archaeology to decipher existing code, you simply change the reactive expressions as needed. A new dependency graph is mapped out to ensure changes are automatically used, in the proper sequence, across all relevant transactions.
Production Use Considerations
Every programming model has its limits. To address requirements outside the database domain (e.g, sending email, application integration. etc.), reactive expressions must integrate the declarative model with an imperative model, using a familiar programming language. With this capability, the vast majority of database apps can leverage RP.
A better development approach with resource overhead and poor performance will never make into production. It’s expensive and users won’t tolerate it. Good RP implementations use best-practice patterns for reducing network and database traffic to scale.
Reactive captures the meaning of the underlying data and enforces consistency by ensuring that this meaning is automatically re-used in the proper circumstances. RP enables you to reduce time scales for building applications, while easing the maintenance burden, improving quality and ensuring scalability.
Val Huber is CTO of Espresso Logic.
Top image: nenetus/Shutterstock