At this point, the preceding functional programming examples should convince any reader that data-driven programming of in-move data in many programming scenarios may achieve a higher performance than imperative programming could ever do.
Reactive programming is the art of programming the propagation of changes. Think of an Excel function =A1+B2. This function will always contain the sum of the two cells; it will never contain a raw (or static) value that represents the sum at a specific time. It will always contain the updated sum of the two cells' values. Anytime the related cells raise a value update event (A1 and A2), the resulting cell will update the sum and show the new value. In C#, a similar behavior is available in a computed property, where the result is available only as a computation instead of a state value:
public double TotalAmount { get { return 10d * 10d; } }
Obviously, reactive programming is not simply a function or computed property. Specific designs and technologies are available and needed to develop using reactive programming. Let's start understanding such a new paradigm by reading its idea and its main characteristics and the available languages and idioms.
The reactive programming experience starts with the reactive manifesto, the idea, the vision, the goal, and the overall design that should be behind any reactive application.
The manifesto is available here:
http://www.reactivemanifesto.org.
The vision is actually simple: modern application needs are incompatible with widely used (and often legacy) designs and architectures. The goals are lovely; they are as follows:
- A message-based overall design with improved lose-coupling (between application and external modules), improved horizontal scalability, high responsiveness, and graceful failure handling.
- A high scalability rate means the ability to scale out with a thin overhead in a sessionless design. This traduces in the ability to handle a huge amount of tasks all together by using multiple computational systems.
- Improved responsive design because of the event-based design. The whole application will react to any request in a short time, letting the
Observer
module do its job without having the subject wait for completion time. - Improved resiliency design because of the ability to gracefully handle applications and user faults thanks to execution context isolation, software module and data replication, and other features.
From this overview of the manifesto, you learned that reactive applications are all about the asynchronous programming of data and event messages flowing between multiple computational isolated stages.
The programming experience
Reactive programming means programming reactions to asynchronously streamed events. This means programming components that receive and produce messages. In between, we can add a transformation or a filter, or components that only produce or receive messages. Sometimes, in other programming paradigms, such message routing components are called message pump.
A time-based programming is drastically different from static data programming. For example, think of an invoice. Instead of programming its data (the total, the VAT amount, and other values), we will handle how the invoice changes in time.
Let's look at the difference, as follows:
The main difference is that, in the reactive style, we have a time-based system, that is, we know exactly what the total invoice value at T3 or T2 was, even years later.
In database programming, there is the ability to create time-based tables. In reactive programming, we do something similar, but at an improved level because in reactive programming, anything is time-based and asynchronous.
A typical explanation about persist the result or all the actors of a function is that, when we persist each message, we do something more real and more similar to how data originates. While we persist the result as a data state, we persist something easier to read later. This second choice is an easier way of programming and is often less disk consuming within a database server, although it may bring unwanted mistakes and further updates of data-state because of some events or needs.
A simple example is available in any Excel worksheet. If Excel was not reactive, at the A1 cell value update, the A2 cell formula pointing to A1 would not be updated, causing the developer to do more work later and exposing this work to a high error rate. Luckily, Excel is reactive; this means that any time we change any cell value, any formula will update its value.
This means that, in more cases, reactive programming behaves and performs better than state-driven programming, but this is not a universal solution. There are cases when state-driven programming is better than anything (typically, when dealing with other state-driven systems), and there are cases when we need to create a lightweight state to improve performances such as caching function results and so on.
When dealing with reactive programming, there are three kinds of message we can deal with. They are as follows:
- A value message containing a new valid value
- An error message containing an error that will flow to any message consumer
- A completed message that signals that the flow is ending
The daily reactive programming receipt contains a lot of alterations to the flow of the subscribed streams, such as event filtering, event composition from multiple streams, event routing, event buffering (compacting multiple similar events occurring quite at the same time), event mapping (converting an event data to another one), and so on.
Change propagation and cancellation
Whenever a new message containing a value flows throughout a stream, a change propagation occurs. It is not a simple binary message containing some values; this message actually informs of a data change. As we are dealing with a value of some types and are programming a time-based system, this flowing data assumes the name data-in-motion.
Note
Data-in-motion is data in real time, such as an application insight stream.
Analysis of such data happens on the same data stream, although some stages later. Data-in-motion messages may translate into other messages by morphing or mapping to other messages, but the original message can never change.
Data-in-motion starts flowing by itself in a push style. We do not need asking for a refreshed value (pull style) simply because, in a reactive application, data always flows from the subject (the data producer) to the subscribing observers, notifying always any new value with the change propagation. This means that any observer will always have the last value. Obviously, this design has a heavy constraint: we need to be always online with the data source.
However, in stateful systems, the change propagation may not occur. We simply ask for refreshed data, but in the meantime, we can work with disconnected (offline) data.
There are multiple examples of pull-based systems working with data streams. For example, any CNC system gives updates (push way) on data changes in addition to the pull-based way of reading data.
Change propagation introduces another interesting feature: the ability of specifying a timeout value by which any elaboration must occur, otherwise it is cancelled. Similar is the ability to flow a specific cancellation message to request the premature end of executing elaborations against a data changed message.
These features are now available to any stateful system thanks to Task Parallel Library (TPL) that extends the low level thread API available in the CLR with specific task-oriented features, such as the task cancellation. However, not everything may execute within a task
class, while in reactive programming, we may always have the ability to cancel a value-changed message elaboration.
Linguistic characteristics
Developing applications by using the reactive programming paradigm means applying to a specified paradigm and nothing else. There is not a specific language to use to comply with reactive programming tenets. Thus, it is possible to create a reactive application by using a specific reactive-oriented language to design the overall application's design and, later, write each computational node (a computational stage on the path of the data stream) in imperative, procedural, or object-oriented programming. This is usually called an implicit reactive programming language. While languages that accept only their own constructs or components are explicit reactive programming languages.
In the Microsoft universe, an implicit reactive programming style designer is available in the BizTalk Server SDK in the map (transformational graphical-based flowchart) designer. Here, specific transformational components, called Functoids
, while there is the ability to use plain
.NET languages or external .NET Assemblies (libraries). Effectively, the BizTalk Server has a lot of reactive programming although different.
A reactive-based programming language may be static or dynamic, exactly as it happens for nonreactive languages. In this context, a static language is the one that statically links multiple nodes between the others. While in a dynamic reactive programming language, these links become dynamic routes that may change because of a specific logic, giving a message of the direction of a node or another with the ability to change direction per message.
A mandatory feature of any reactive programming language is the ability to manipulate different streams with features, such as the ability to merge multiple streams like a join
statement makes against multiple relational tables or divide a single stream into multiple ones.
Another must-have feature is the ability to configure a Quality of service priority list of messages within the stream. This is because not all the messages have the same priority. A canonical example is about any real-time control console of hardware systems, where pressure on any key on the keyboard or any button on the board must give an immediate feedback to the user on the screen or with a light or a sound. Conversely, an informative message or notification from the system to the user does not need the same priority, because the use may simply be somewhere other than at the front of the console. This means that some millisecond of delay is valid.
Programming languages and frameworks
There are many reactive programming and compliant form programming languages available to any developer with any background.
For any Microsoft-oriented developer, the obvious choice is learning the Microsoft Reactive Extensions—a library for adding all the needed features to .NET and other languages to comply with reactive programming. This is available for .NET as Rx.NET, for JavaScript languages as RxJS, and for Visual C++ developers such as RxCpp.
For JavaScript developers, there are multiple libraries adding reactive features, such as Reactive.js, React (by Facebook), Node.js, ProAct.js, and others, available for client development in ASP.NET MVC or WebForms. Links to some of these libraries are listed here:
Reactive programming approaches
When programming using the reactive programming paradigm in implicit style, we can mix different subparadigms into the computational stages that will handle stream processing. As already mentioned, implicit reactive programming languages give the ability to design an overall (or part of) reactive application and add nonreactive programming scripts or blocks into components, controls, or modules that will execute single stream logic, such as aggregation, filtering, mapping, and so on.
In these computational blocks (that represent data flow stages), we can use functional programming, object-oriented programming, and imperative or declarative programming. For most cases, such as the BizTalk Server mapping, a simple C# script (single or multiple lines) is available as a programmable component for a single stage. In the case of using such features extensively, the paradigm takes the name imperative reactive programming.
When we use general-purpose programming languages, such as C#, we will obtain reactive programmability by adding libraries to the core base classes like Rx.NET
.
The C# now available gives us the ability to use object-oriented reactive programming. This means that we can use object-oriented programming in a single reactive stage or inverse the situation by using reactive programming in a single module or class or an object-oriented application. We can use both the alternatives together in the same application too, although this last choice will make the maintainability of the code difficult for the developer.
The more pure visual reactive programming style instead gives the developer only visual components to do their job. This is what happens in the SSIS data flow diagram, although not a reactive programming language.
If, in the computational stage, we make use of functional programming or languages, such as F#, the overall paradigm will take the name functional reactive programming.