Reactive Programming for .NET Developers

5 (1 reviews total)
By Antonio Esposito , Michael Ciceri
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. First Steps Toward Reactive Programming

About this book

Reactive programming is an innovative programming paradigm focused on time-based problem solving. It makes your programs better-performing, easier to scale, and more reliable.

Want to create fast-running applications to handle complex logics and huge datasets for financial and big-data challenges? Then you have picked up the right book!

Starting with the principles of reactive programming and unveiling the power of the pull-programming world, this book is your one-stop solution to get a deep practical understanding of reactive programming techniques. You will gradually learn all about reactive extensions, programming, testing, and debugging observable sequence, and integrating events from CLR data-at-rest or events. Finally, you will dive into advanced techniques such as manipulating time in data-flow, customizing operators and providers, and exploring functional reactive programming.

By the end of the book, you'll know how to apply reactive programming to solve complex problems and build efficient programs with reactive user interfaces.

Publication date:
July 2016
Publisher
Packt
Pages
276
ISBN
9781785882883

 

Chapter 1. First Steps Toward Reactive Programming

Thanks for buying this guide to Reactive Programming and Reactive Extensions for .NET.

This book will give you an expert overview about the magical world of programming, also known as programming live data or real-time data, instead of programming static data as happens with any other usual programming paradigm.

Do you know Microsoft Excel?

Excel is a software that lets you write raw numbers and functions in a proprietary mathematical form or through a simple scripting programming language (VBA).

The magic happens when you write an Excel function, such as =A1+A2, which means that the current cell value will be the sum of the values contained in the A1 and A2 cells.

This simple function creates a subscription to the events of cells A1 and A2. This means that any time you update the value of any of these two cells, the function will return a new value. This is the simplest example of functional and reactive programming available.

Now that we have an idea of what reactive is, it is easy to explain that this book will guide developers with any background knowledge about reactive programming toward the understanding of the reactive programming paradigm with a lot of examples of using Reactive Extensions (Rx) for .NET.

This chapter will give an overview of what Reactive Programming (RP) is and how it works, starting with programming paradigms and later diving into the RP structure following the schema:

  • Programming paradigms

  • Dataflow programming

  • Functional programming

  • Reactive programming

 

Programming paradigms


Once upon a time, programming languages where extremely different from the ones we have today.

The first-generation programming language was the machine-code one. It was made against the hardware itself, and for most, programming in such way meant creating hardware solutions like moving jumpers or switches or adding/removing cables.

The second generation of programming languages was the assembly language (such as Assembler). The name was related to the assembling stage of these languages into a machine level one that is able to run in the CPU execution pipelines. This language generation was the first made with text, although it was tightly coupled with hardware architectures.

The third generation of programming languages started the age of English-like languages that were able to run on top of hardware specifications with their own instruction set, no longer coupled with the lower hardware level. It started the reusability era.

These languages were not made in real CPU executable code. Programmers did their job in a high-level programming language that, after the compilation stage, translated into a lower-level one that was able to run into the CPU execution pipeline. At the beginning of the high-level programming era, the most diffused languages where IBM ® Fortran and COBOL.

Modern languages, such as .NET, Java, and C/C++, are all of the same generation as their grandparents of the 1950s. Obviously, the current languages have improved features and abilities, because of the long evolution time.

The main differentiation between the previous programming languages and the current ones is the programming paradigm—something like a programming approach of structured methodology that changes the way a programmer creates software.

The oldest one is the imperative  programming paradigm. It is made of a direct sequence of steps, usually numbered from 1 to N, that simply executes in a forward-only fashion. In these stages, the ability to jump forward or backward with commands, such as GoTo, was definitely a killing feature, while now, with modern programming paradigms, it is absolutely avoided. By programming with such an approach, a simple application made to sum two values was a simple sequence of steps, or instructions, that altogether achieved the desired goal. Here is an example in C#:

Console.WriteLine("Step counter: RUNNING"); 
 
Console.WriteLine("Write the starting value"); 
var startingValue = int.Parse(Console.ReadLine()); 
 
//value entering starting point 
REPEAT: 
Console.WriteLine("Write the ending value"); 
var endingValue = int.Parse(Console.ReadLine()); 
 
if (endingValue <= startingValue) 
{ 
    Console.WriteLine("ending value must be greater than starting value"); 
    goto REPEAT; 
} 
 
//this counter represents the distance between startingValue and endingValue 
var counter = 0; 
 
//increments the counter until needed to reach the endingValue 
COUNT: 
if (endingValue > ++counter + startingValue) 
    goto COUNT; 
 
Console.WriteLine("You need {0} steps to reach {1} from {2}", counter, endingValue, startingValue); 
Console.WriteLine("END"); 

As you can see in the preceding example, the whole program is only a list of steps where the current actor is at once the computer asking for something on the console and the user writing some response on the console. The program is unable to do multiple things together. Either the computer places a question to the user, or the user enters a digit or something on the keyboard to give the computer a command or some data.

Note

C# is a general purpose programming language supporting imperative, procedural, declarative, object-oriented, component-oriented, service-oriented, and functional programming paradigms all together.

There are no interaction constructs (for, for...each) available in imperative programming, and there is no code factorization into subroutines able to abstract and make reusable single portions of code.

The check logic against the user value is available through a simple GoTo statement that is able to move the control's flow pointer (the actual execution row) to a newly desired position. Regarding this check logic, the execution flow simply goes back to some previous line executing and entering a destination value.

Similarly, when it is time to count the distance between the two given values, the logic again uses the GoTo statement, changing the current state of the counter variable to the updated value. It is the last time the variable will contain the required result.

Note

It is interesting that throughout the imperative programming paradigm, C# is even faster than when programming in an object-oriented one, because of the higher usage of the stack memory, instead of the heap memory that is slower.

If you are interested in code optimization and high performance programming, you may find it interesting to read my other book Learning .NET High Performance Programming.

In imperative programming languages, such as Fortran, Pascal, or COBOL, there was a number for each row to give developers the ability to create interactive constructs, such as recurring jobs or any other interaction logic, by jumping between rows.

Obviously, this choice is not available in modern general-purpose languages, such as C#, which give the same feature with the use of a label (in the preceding example, we used Repeat and Count) instead of the row number. The result is the same.

To understand imperative programming, we must understand the meaning of the state. An application's state is the sum of all the data usually available within the code through fields, properties, and variables. When we started the preceding code, the application state was empty (we will ignore the Common Language Runtime (CLR) stuff that is in the memory together with our data). During the execution, some variables (startingValue and endingValue) became available by asking data from the user.

Later, a counter variable will make the most of the work to find the distance between the two numbers. What is the heart of the imperative programming paradigm is the status change that the code makes against available variables. As visible, the counter variable becomes incremented until the wanted value is found.

This status change still happens in other paradigms such as procedural-programming or object-oriented programming, although these paradigms bring to a higher level abstraction of data structures or a better code reusability.

 

Dataflow programming


Changing the application state is not something wrong by itself, but there are different programming approaches that may produce better results and together give the developer a more pleasant working experience. A typical use case happens when we deal with in-move data (or living data or data stream), where we may find that using interaction logic constructs that change state, such as if, for, and so on, is a poor performing choice together with a poor design. In-move data is any kind of data stream, such as a video stream, an application insights stream, and so on. Because of its statelessness, it is obvious that a stateless programming approach offers better results than a state-driven one.

We are used to dealing with static data, such as a variable, a database, or anything else such as some binary- or string-based data. All such data is data-at-rest, static data, or simply data.

As an example if we execute a select statement against a relational database, we will always have a result set containing the exact value contained in the database table at the specific time we executed the query. A second later, the table could experience an update statement that could change any row's data without the first client (the one executing the select statement) receiving an update on such data changes. To address these kinds of data changes without having to face issues between different relational database clients, there are optimistic and pessimistic concurrency checks (a bit outside the scope of this book). Obviously, the less we need to synchronize code to access a concurrent resource, the better our code will perform.

In imperative programming, control-flow is responsible for the good execution of the application. Such flow is usually made of multiple code rows that do something on input/output ports and somehow alter the application's state until the desired result is achieved, whereas in dataflow programming, data flows in and out of the different stages of a flowchart, as it behaves in a workflow.

Obviously, the different types of programming will greatly change the developers' experience and programming capability of the language. It is very difficult (and conceptually a bit wrong) to compute something by executing some interaction logic in data flow programming, because this kind of programming is simply outside the core design of the programming paradigm.

A practical example can be seen in data integrational , Extract, Transform, and Load (ETL) workflows, such as those available in SQL Server Integration Services (SSIS), as shown in the following screenshot. An ETL workflow has the task of reading (extracting) data from a data source (relational or not) and mapping (transforming) such data by grouping or aggregating it with other data sources or by executing transforming functions. Then, data flows (loads) into a target data store (relational or not) for future simplified access. SSIS is the tool for designing these kinds of workflows within the SQL Server Business Intelligence suite.

A SSIS dataflow task doing some transformations on data from a relational database

Generally speaking, outside the SQL server-oriented implementation of SSIS, within data flow programming, instead of having a huge code base in a high-level code, we have something like a data workflow. A digraph (directed graph)—an ordered version of a usual flowchart. Here is an example:

A simple representation of a dataflow digraph made of three recurring stages

Within the Microsoft universe, the only data flow programming compliant language is the Microsoft Visual Programming Language available for the Microsoft Robotics Developer Studio environment for robotics programming. Instead, SSIS simply uses data flows to handle data integrations between databases.

Statelessness

The unavailability of the state, a key concept of data flow programming, is the opposite of what happens in all imperative or object-oriented programming-based applications. This behavior drastically changes the programming experience.

A stateless design never stores (temporary or persisted) application or user data with the need of changing it in time for computational needs.

We cannot use temporary variables to store changeable values, such as the total of an invoice. We cannot use an index to jump around a collection or an array, and we cannot iterate it. Obviously, if we need to write a function that needs a variable, we cannot use a variable anymore; in other words, we can't use variables that act as a business logic state persistence.

When we write a function, the data will simply have an origin, a target, and one or multiple transformations in multiple stages.

Thanks to this stateless design of the whole application, it is easy to see that each stage can run on a different thread together and each input or output endpoint can run on another thread and so on. The stateless design is the key that makes the design able to scale out quite perfectly according to Amdahl's Law.

As well as performance results, a stateless design brings higher testability rates of the whole application (bear in mind ASP.NET WebForms versus ASP.NET MVC) together with a more modern approach in programming style that avoids the use of interactive loops such as for, for...each, and relatives.

The data-driven approach

The last evolution of the imperative programming paradigm is object-oriented programming. Such paradigms request we model our business world into a high-level domain model. This means that, in our code, we will find an object representing any living entity of our business model; a invoice or a customer are examples of such objects. Such models drive business logic. They do not need to be persisted in a one-to-one representation from the model to the persistence store (usually, a relational database). This approach is called domain-driven design. The opposite of such an approach is the data-driven design that makes direct actions against data without a real discrimination between data and business.

Because of the intrinsic behavior of dataflow programming, a data-driven design is the natural choice when designing a solution based on such programing paradigms.

But in the modern .NET-based programming style, the use of business-related entities in the various stages of the dataflow execution is available and suggested too.

Data streams

A data stream is the flow of some data in time, usually of a unique format, that is available to one or multiple readers.

Examples are television video streams, YouTube video streams, Twitter or RSS feeds, Azure EventHub, and so on. Those who are used to C# programming will remember the namespace System.IO that contains different classes made for stream programming, such as BinaryReader/BinaryWriter that makes available streaming any CLR low-level type or StreamReader/StreamWriter that makes available streaming any text supporting various encodings from ASCII to UTF32.

In other words, a data stream is some data in a specific time instance. Time is the key concept for understanding a data stream. It is all about running data or in-move data. Without the time component, data can never flow in a stream.

Depending on the stream, it may support seeking operations, such as the ability to go forward and backward along the stream to start flowing data exactly at the desired time. Television video streams do not support such a feature. Microsoft Azure EventHub (a data stream) instead supports the feature in a configured time window of usually some hours.

The Azure EventHub is a paralleled stream service for streaming any data, usually used for Internet of Things (IoT) devices, telemetry values, or diagnostic purposes as an application insights collection. A similar choice within the Azure offering is the IoT Hub, another streamed service totally oriented to IoT devices that support more specific protocols.

Observer pattern

The Observer pattern, is a published subscribe style pattern; it defines the ability to register for data change or event signaling. Although written words may be something new to read about, the observer pattern is something absolutely overused in the event-driven programming paradigm of the Microsoft oriented languages in the last 20 years.

When we handle the Button click event, either in Visual Basic (from version 1 to 6) or in modern .NET Windows Forms, WPF applications, or even ASP.NET applications, we are simply using an implementation of the observer pattern.

The pattern defines an event generator, also known as subject, that fires the event and one or multiple event listeners or observers (in .NET, also known as event handlers) that will do something in reaction to the data state change or event signaling.

When dealing with data flow programming, the observer pattern is the one responsible for the acknowledgment between stages of the new data available. Each stage informs the following stages of new data available with a signal. Stages do not know about the overall design of the digraph. They simple signal the new data availability event; all subsequent stages that are observing the previous one for new data will be then acknowledged. This design makes useless the need of an overall data state, thus the design is stateless. Each stage observes or is being observed. That is all. Such data flowing between stages are data streams.

 

Functional programming


Functional programming is a programming paradigm that relies on the functional transformation of data between stages, not so different from the dataflow programming we just saw. The main addendum to the data flow paradigm is that, within the functional one, there is a specific functional transformation, while in the data flow paradigm such transformations occur in a specific idiom-based way that can be functional-based, script-based (imperative paradigm), component-based, or anything else.

In functional programming, data is always immutable and functions are responsible for data transformation. Anything in functional style is a data transformation. Although a decisional statement (if) cannot exist, a functional transformation from data containing predicate to data containing the Boolean result may occur:

//procedural style 
var sourceData = new { TotalAmount = 12345.67, PaidAmount = 12345.67 }; 
if (sourceData.PaidAmount == sourceData.TotalAmount) 
{ 
    //do something 
} 
 
//functional style 
//a simple enumerable 
new[] { new { TotalAmount = 12345.67, PaidAmount = 12345.67 } } 
//transformation block 
.Select(x => new 
{ 
    x.TotalAmount, 
    x.PaidAmount, 
    CanContinue = x.TotalAmount == x.PaidAmount, 
}) 
//if 
.Where(x => x.CanContinue) 
//do something 
.ToArray(); 

A widely known design pattern is the data mapper one. This design tries to decouple and translate objects of a layer (for historic purposes, the domain layer) into another one (for historic purposes, the data model one).

Here is a simple procedural (imperative with subroutines) programming style example in C#:

class Program 
{ 
    static void Main(string[] args) 
    { 
        //retrieve invoices from DB 
        var invoicesFromDB = GetInvoicesFromDB(); 
 
        //map those invoices to business model objects 
        var invoicesForBusiness = ConvertInvoicesForBusiness(invoicesFromDB); 
    } 
 
    /// <summary> 
    /// Reads invoices from a (fake) database 
    /// </summary> 
    /// <returns></returns> 
    static InvoiceFromDB[] GetInvoicesFromDB() 
    { 
        var result = new InvoiceFromDB[3]; 
 
        result[0] = new InvoiceFromDB 
            { 
                CustomerName = "Mr. Black", 
                Number = "100/2015/BBC", 
                BaseAmount = 24560, 
                VATMultiplier = 1.22, 
            }; 
 
        result[1] = new InvoiceFromDB 
            { 
                CustomerName = "White Co Ltd", 
                Number = "240/2015/BBC", 
                BaseAmount = 422480, 
                VATMultiplier = 1.22, 
            }; 
 
        result[2] = new InvoiceFromDB 
            { 
                CustomerName = "Green Woods inc.", 
                Number = "22/2015/BBC", 
                BaseAmount = 8500, 
                VATMultiplier = 1.22, 
            }; 
 
        return result; 
    } 
 
    /// <summary> 
    /// Converts invoices from database to invoices for business needs 
    /// </summary> 
    static InvoiceBusiness[] ConvertInvoicesForBusiness(InvoiceFromDB[] source) 
    { 
        var result = new InvoiceBusiness[source.Length]; 
 
        for (int i = 0; i < source.Length; i++) 
            result[i] = new InvoiceBusiness 
            { 
                Number = source[i].Number, 
                CustomerName = source[i].CustomerName, 
                BaseAmount = source[i].BaseAmount, 
                VATMultiplier = source[i].VATMultiplier, 
            }; 
 
        return result; 
    } 
} 
 
public class InvoiceFromDB 
{ 
    public string CustomerName { get; set; } 
    public string Number { get; set; } 
    public double BaseAmount { get; set; } 
    public double VATMultiplier { get; set; } 
} 
 
public class InvoiceBusiness 
{ 
    public string CustomerName { get; set; } 
    public string Number { get; set; } 
    public double BaseAmount { get; set; } 
    public double VATMultiplier { get; set; } 
    public double Total 
    { 
        get { return BaseAmount * VATMultiplier; } 
    } 
} 

In a functional style, the same program will benefit from the use of the enumerator pattern (for static data such as this one) or triggers that will notify for observed data changes.

Later, the transformation logic will take place within a function.

Here is the same preceding example in a functional style made with LINQ querying in C#:

class Program 
{ 
    static void Main(string[] args) 
    { 
        var businessInvoices = 
            //stage 1 - read db data 
            GetInvoicesFromDB() 
            //stage 2 - make transfomation 
            .Select(dbinvoice => new InvoiceBusiness 
            { 
                Number = dbinvoice.Number, 
                BaseAmount = dbinvoice.BaseAmount, 
                CustomerName = dbinvoice.CustomerName, 
                VATMultiplier = dbinvoice.VATMultiplier, 
            }) 
            //force execution of the whole enumerator 
            .ToArray(); 
    } 
 
    static IEnumerable<InvoiceFromDB> GetInvoicesFromDB() 
    { 
        yield return new InvoiceFromDB 
        { 
            CustomerName = "Mr. Black", 
            Number = "100/2015/BBC", 
            BaseAmount = 24560, 
            VATMultiplier = 1.22, 
        }; 
 
        yield return new InvoiceFromDB 
        { 
            CustomerName = "White Co Ltd", 
            Number = "240/2015/BBC", 
            BaseAmount = 422480, 
            VATMultiplier = 1.22, 
        }; 
 
        yield return new InvoiceFromDB 
        { 
            CustomerName = "Green Woods inc.", 
            Number = "22/2015/BBC", 
            BaseAmount = 8500, 
            VATMultiplier = 1.22, 
        }; 
    } 
} 
 
public class InvoiceFromDB 
{ 
    public string CustomerName { get; set; } 
    public string Number { get; set; } 
    public double BaseAmount { get; set; } 
    public double VATMultiplier { get; set; } 
} 
 
public class InvoiceBusiness 
{ 
    public string CustomerName { get; set; } 
    public string Number { get; set; } 
    public double BaseAmount { get; set; } 
    public double VATMultiplier { get; set; } 
    public double Total 
    { 
        get { return BaseAmount * VATMultiplier; } 
    } 
} 

What makes the preceding example different from the previous one is that the GetInvoicesFromDB method returns an enumerable collection. Although an array is enumerable too, an array is a finite item collection. In the procedural example, all contained objects must already be in our memory before the method returns values creating a huge data state. Later, in the imperative example, such a state will be changed by the other method ConvertInvoicesForBusiness that is responsible for creating business objects containing business stuff (Total in the preceding example).

In the functional style example, instead of having an interactive statement that processes data in the memory, we will simply write a new function able to transform the data-oriented object into the business-oriented one. This transformation is declaratively defined with a function. The function simply states how the transformation must happen. Thus, to trigger the concrete computation and the storage (in memory) of the transformed data, we will use the .ToArray() LINQ extension method that will evaluate the functional result and store such results into a finite item collection, the array.

Note

We will delve more deeply into functional programming and functional reactive programming in Chapter 8 , F# and Functional Reactive Programming, and Chapter 9 , Advanced FRP and Best Practices, of this book.

 

Reactive programming


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.

Reactive manifesto

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:

Time stages

Imperative style

Reactive style

T0

A new Invoice object is instantiated. Its total is 0.00.

A new Invoice object is instantiated.

T1

A product line is added to the invoice. The invoice's total changes to 450.00.

A new InvoiceAdditionItem object is instantiated.

T2

A product line is added to the invoice. The invoice's total changes to 1450.00.

A new InvoiceAdditionItem object is instantiated.

T3

A product line is added to the invoice. The invoice's total changes to 2450.00.

A new InvoiceAdditionItem object is instantiated.

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.

 

Further reading


Although this book is about reactive programming, the most transforming modules we will write will observe the object-oriented programming paradigm. This is a good thought. Simply consider the following assertion: the best programming style or language is the best only in a few conditions. Accordingly, try your programming style to the task you're facing.

Here are some suggestions:

  • Patterns of Enterprise Application Architecture, Martin Fowler, Pearson Education, Inc

  • Domain-Driven Design: Tackling Complexity in the Heart of Software, Eric Evans, Pearson Education, Inc

 

Summary


In this chapter, we had the opportunity to give an overall description of reactive programming frameworks and languages. Specific key concepts and designs are available for this programming paradigm.

In the next chapter, we will put into practice all such theoretic notions, writing examples in pure C#, and trying to understand all the key concepts and designs explained here.

About the Authors

  • Antonio Esposito

    Antonio Esposito is a Microsoft Certified Trainer, software architect, father, son, and lover of cooking and eating. He has been addicted to computer programming from age 8, a developer since 2002, and a speaker from 2010. He has moved across Europe in the last fifteen years working as freelance consultant or speaker for companies such as UniCredit Bank, Ferrari F1 Racing Team, Microsoft Italy, IBM, and many others. He actively attends as a speaker at a lot of conferences, such as MCT Summit and WPC Italy. He is already an author for Packt with Learning .NET High Performance Programming in 2014.

    Browse publications by this author
  • Michael Ciceri

    Michael Ciceri is a technology consultant in .NET Framework and Microsoft. He is a functional programming, mathematics, technology, psychology, and science enthusiast. He started as an autodidact and passionately became an analyst and software developer in several areas, such as image processing, banking ATM services security, intranet back end, app monetizing. Recently, he has been working on the analysis and development of functions in the core application to solve problems or improve capabilities.

    Browse publications by this author

Latest Reviews

(1 reviews total)
Excellent

Recommended For You

Book Title
Access this book, plus 7,500 other titles for FREE
Access now