A recent post on Reactive Programming triggered discussions about what is and isn’t considered Reactive Logic. In fact, many have already discovered that Reactive Programming can help improve quality and transparency, reduce programming time and decrease maintenance. But for others, it raises questions like:
- How does Reactive differ from conventional event-oriented programming?
- Isn’t Reactive just another form of triggers?
- What kind of an improvement in coding can you expect using Reactive and why?
So to help clear things up, here is a real-life example that will show the power and long-term advantages Reactive offers. In this scenario, we’ll compare what it takes to implement business logic using Reactive Programming versus two different conventional procedural Programming models: Java with Hibernate and MySQL triggers.
We intentionally chose a set of requirements that is small and familiar, yet sufficiently complex to warrant extrapolation to larger systems. We implemented the business logic to place an Order. The schema is simple; Customers have Purchase Orders (the name Orders is used interchangeably) consisting of Line Items, each which references a Product.
These are the requirements on the infamous “cocktail napkin,” as they might have been captured in discussions with business users:
The requirements above dictate that the following 11 related use cases must be implemented:
Imperative Programming Takes 200 – 500 Lines
In programming this simple “cocktail napkin spec,” developers have to consider all of the 11 use cases. When we asked one of the developers to write code for this scenario, the logic in Java/Hibernate required 500 lines (see code here) and with MySQL triggers, it took 200 lines (see code here).
As a point of reference, let’s look at the trigger (event) implementation. The trigger is notified that a row is changed, but the trigger itself must provide the logic for:
- Watch to determine if column values have changed (see trg_update_order)
- React to propagate changes to relevant data (see sp_adjust_order_totals), and
- Persistence to invoke SQL
Each table has a trigger to watch for changes to data referenced elsewhere. This logic can become progressively more complicated as system size increases and represents unfortunate coupling between domain objects.
There are several routines likes this for reacting to changes:
The Reactive Programming Approach
Now let’s compare the procedural programming approach with Reactive. First, we’ll briefly describe the Reactive Programming approach for database transaction logic. Reactive Programming, similar to a spreadsheet, automates the define, watch and react process:
- Reactive Expressions define the invariant state of a database column, such as the Customer.Balance.
- The system parses Reactive Expressions to determine referenced columns and establishes watches on these, such as the Paid flag on Orders.
- It then reacts by adjusting the balance as required (e.g. to decrease the balance when the Order is paid). Persistence is handled automatically in a well-designed Reactive system.
Much of the power in Reactive results from chaining, where the result of one Reactive Expression is used by another. For example, the Orders.amount total is itself defined by another Reactive Expression, sum (lineitem.amount). As in a spreadsheet, chaining expresses complex logic in a remarkably simple manner.
Just 5 Lines in Reactive Programming
Reactive Programming encapsulates logic expressions into the relevant columns, so your logic is automatically invoked on any changes to those columns, regardless of the use case. In other words, the logic is automatically reused over each use case. All of the reads and writes to the database (persistence) are handled automatically.
So, you simply input the “Cocktail Napkin Requirements” as a series of Reactive Expressions. All the use cases above are addressed with Reactive Logic. Persistence is transparent. The “Cocktail Napkin Requirements” are fully executable.
From the metrics captured in this example, you can see that Reactive Programming enables:
- Faster Time to Market – Reactive provides a significant advantage in the construction of backend database logic; five lines of code compared to 500 lines using Java or 200 lines using triggers.
- Reduced Maintenance – Reactive expressions are automatically sequenced per dependencies. Unlike conventional systems where significant time is required to understand existing ordering, you simply change the logic. Dependencies are rediscovered and reflected in execution order.
- Enhanced Quality – Encapsulating logic into database column definitions (across a variety of possible architecture) ensures automatic reuse of the logic across use cases. This eliminates an entire class of “corner-case” bugs.
- Transparency – Business users can read and understand Reactive Expressions and spot errors, something that is not practical with triggers and stored procedures. Transparency also reduces the documentation required and can improve overall quality.
In conclusion, Reactive appears to be a very promising technology for reducing delivery times, while improving system quality. And no doubt this discussion may raise other questions on extensibility and performance for Reactive Programming.
Val Huber is CTO of Espresso Logic.