A story about reactive thinking


John Schnobrich ©  Unsplash

Back in 2014, Max and Simon thought about starting a business to maximise user experience with modern web development. When clients came up with an idea, they did everything by themselves (including research, estimations, design, development, marketing and accounting). But soon they realised that they needed to change their structure to deal with the increasing work load. This story is about modelling of their company in a reactive way.

Reactive programming Process

Reactive Terms

Before we start refactoring, let’s clarify some terms up front. In the reactive world there are observables. These observables are a stream of data. One can subscribe to that stream and will then receive all the new items passed into the stream. Imagine you are watching TV. Then you receive the current image and sound as a hot stream. Hot means that it is even active when you do not watch or listen to the stream. Whereas when you watch Netflix the stream is cold. It starts only if you click on a movie and emits only that movie. Observables are cold by default. In application terms a hot observable can be a stream that listens to user inputs like clicks on specific elements. A cold observable for example can be an http request to a web server. These observables are by convention prepended by $ sign in the code.

In addition to these behaviour, observables are chain-able. Each observable has a pipe member function. It can handle so called operators as its arguments. You can think of an operator as a modification observable, but very important: As an observable. The observable is a class that looks like

class Observable { producer; constructor(producer) { this.producer = producer; } subscribe(observer) { return this.producer(observer); } pipe(...operators) { return operators.reduce((source, next) => next(source), this); } }

In this class you can notice that the observable only executes the producer function if the subscription is executed. In addition to that, the pipe function receives a list of operators which will be executed by step by step with each step as the source of the next step.

The client, the company and the app

In RxJS software terms the client is an observable that modifies the environment stream. If something changes in the environment, like the market situation, the client is going to receive it automatically. Based on this information he builds up a new application idea and figures out its requirements and the available budget.

const client$ = environment$.pipe( map(environment => { // analyze the market // find a new idea based on the market knowledge // return {idea, requirements, budget} })

The map function takes a mapping function. This function maps the environment to other properties based on the developers needs. The company of Max and Simon currently looks like the following observable. They are just two people and have to manage every task by themselves. This results in a huge lists of tasks and can become very messy.

const company$ = zip( clients$, environment$ ).pipe( map(([{ requirements, idea, budget }, environment]) => { // reject or accept the idea based on // environment, budget and requirements // transform the idea to a PoC // estimate risks and time schedule // destructure problem to small tickets // implement designs and software // return an application }) );

When the application is completed, it will be forwarded to the testers before it will be published in the Playstore. The testers receive the application and the clients requirements and check if every part is sophisticated. If it is not ok, it will reject the application and do not emit it to further subscriptions.

const requirements$ = clients$.pipe(pluck('requirements')); const testers$ = zip( requirements$, company$ ).pipe( filter(([requirements, application]) => application.matches(requirements) ) );

The filter function takes a function that checks if the emitted values (requirements and application) should continue the stream based on true or false values. The playstore should add (or overwrite) new applications (or versions) in the playstore. In addition to that, all customers browsing inside the appstore (subscribe to the store) should receive the same value independently of how many people currently watching the store.

const playstore$ = testers$.pipe( scan((allApps, app) => ({...allApps, [app.id]: app}), {}), shareReplay(1) );

This is the complete process of the image from above in a reactive way of programming. If there is a change in the environment$ the data is going to pass through all these steps reactively. To complete the idea we need to know that every process step only makes sense, if there is at least one subscriber to the store, which means there is at least one subscriber to each observable in the observable chain. Otherwise all parts of the process would not be executed. This subscription process is a very important pattern in reactive programming and is going to be discussed in the following section in detail. Keep in mind that this process might not reflect an actual real-world scenario.

The steps of subscribing

Before we continue building our decoupled stream, it is time to go for a little foray. In the common way of explaining RxJS this progress is not mentioned very often. How subscriptions work: imagine environments$ is a http call to an API which can be requested to receive details about companies, peoples interests and country wealth and then complete. A custom observable making an XHR request would look like

function get(url: string): Observable { const producer = (observer: Observer) => { const xhr = new XMLHttpRequest(); xhr.addEventListener('load', () => { if (xhr.status === 200 && xhr.readyState === 4) { observer.next(JSON.parse(xhr.responseText)); observer.complete(); } }); xhr.open('GET', url); xhr.send(); return () => xhr.abort(); } return new Observable(producer); }

We can use this observable in the RxJS way by defining an observer with the next, error and complete function:

environments$ = get(`https://publicapi.com`).subscribe({ next: (data) => console.log(data), error: (err) => console.log(err), complete: () => console.log('The stream has completed'); });

of course with an imaginary URL. If we subscribe to this observable, it executes the observables so called producer function, which is in this case the XMLHttpRequest and returns the data to the stream with the observers next function, then completes. The subscription method receives either an observer or three callbacks in exactly the same order as above. Let’s take a look on operators and create a custom one.

function map(mapOperation) { return (source) => { const producer = (observer) => source.subscribe({ next: (value) => observer.next(mapOperation(value)) }); return new Observable(producer); } }

In this example map returns a function that has a source argument and returns a new observable. The observables producer function subscribes to the source observable and returns each source observables value with the mapFn applied. You can have a look at the origin RxJS map operator and you will see the similarities. Now we can use the pipe function to chain this observable to the http observable.

const currentTechnologies$ = get('https://publicapi.com').pipe( map((environment) => environment.currentTechnologies) );currentTechnologies$.subscribe({ next: (technologies) => console.log(technologies), complete: () => console.log('Stream has completed') })

So what does the subscription do? You can imagine each entry in a pipe as another observable. It subscribes to the observable that was returned by the map operator first, and afterwards this observable subscribes to the http observable. Then the actual http process is going to be executed (because it is a cold observable and only executes on subscription). If the data arrives it is going to be processed inside the map operator and afterwards it is accessible in the subscription. In addition to that, the http request observable completes and sends it complete state to the map and cuts the subscription between those two. Then the map emits the complete state and the complete function inside the subscription fires. The subscription between the subscribe and the map is now gone as well.

The subscription process is a key concept that we should keep in mind for the rest of the article and whenever we are going to work with RxJS. Let’s continue with the application building process. It is time to get more people involved.

Time to hire


When time was passing by and more clients are asking for software, the founders Max and Simon thought that it would be nice to have a real expert on the technology they want to choose for most of their projects. Also they thought that it was good to have a more structured way for their organisation. The only requirement which must be met is that the company receives an idea and payments from the client and outputs an application. So they decided to hire Kris, an expert Angular developer.

const founders$ = zip( client$, environment$ ).pipe( map(([{idea, payments}, env]) => // Todo // reject or accept // transform the idea based on the environment to // a proof of concept // estimate risks // return parts of the payments, prototype and estimation ) )

As you can see, the both founders Max and Simon significantly reduced their workload and they can focus on accounting, client acquisition and business development. From now on they do not need to know the complete development steps in depth. This is where Kris comes into play.

const developers = ['Kris']; const speedOfDevelopment = developers.length / 1000;const developers$ = founders$.pipe( filter(({payments}) => payments > 0), map(({payments, prototype, estimation}) => // Todo // create subtasks // implement designs // implement application // return application ), delay(1 / speedOfDevelopment) )

He is responsible for creating the application and also for the designs. As more and more projects come up, they decide to reduce the time needed to finish a project by hiring more developers.

const developers = ['Kris', 'Anna', 'Gustav', 'Yens', 'Maria', 'Lennard'];

But hiring more and more developers was not enough. So Max and Simon decided to search for some project leads to manage projects, estimate the schedule and break down the implementation tasks into smaller subtasks. Also Kris wished for some designers to tackle the design tasks.

What have you recognised alongside with the implementation of the developers and? We have decoupled our code of one observable into smaller pieces which are way more simple to handle and less error prune than the big one. And that is a core assumption of reactive programming: you divide bigger parts of the program into smaller parts that act independently. This is what we also want to do with project leads in our example.

Developers in Reactive Programming

Project Leads

The developers did a very good work, but they were a bit unorganised and sometimes they found themselves in finding the best of the best solution fanatically, instead of having a look at the time and focus on the most important parts. Here came the project leads into play. They are in charge of making an overview of all projects and create small tickets for each task. In addition to that, if there are new developers subscribing to the project lead, they should also get the latest tickets.

const projectLeads = ['Emma', 'Markus'];const projectLeads$ = founders$.pipe( scan(({ payments, prototype, estimation }) => // Todo // Append application to either Emma's or Markus' // application stack // Append prototype and estimation to the project // leads internal projects list // Check if project payment ressources are covered , {}), map(({ projects }) => // Todo // Create small tickets for the developers and // (later on) designers // Estimate a time for each ticket // Return tickets ), shareReplay(1) );

The developer’s implementation now needs to be refactored to receive the tickets instead of the full application.

const developers$ = projectLeads$.pipe( map(ticket => // Todo // Implement designs // Implement application // Return application ) );


At this point the applications are technically top-notch, reactive, and use best-practices and latest technologies. Max and Simon are quite satisfied with the results, but there is one key-point missing. The applications look pretty straight forward and like every other developer-onlyapplication. Mostly with a titlebar, a side navigation, some cards and a table. So the leads decide to hire some designers to raise the user experience to the next level.

const designers = ['Agathe', 'Annabelle', 'David'];const designers$ = this.projectLead$.pipe( filter(ticket => ticket.isForDesigners), map(ticket => //Todo // Create design based on tickets // Return design ) );

The developers now can use these pretty designs to implement first-class looking applications.

const developers$ = this.projectLeads$.pipe( filter(ticket => ticket.isForDevelopers), withLatestFrom(this.designers$), map(([ticket, designs]) => // Todo // Implement features/application // Return application ) );

Discover the reactive playground here. You can see, that if you trigger the process by clicking on the “New Environment” the complete stream will be processed again. This coveres a part of the full company

With all the new team members on board, internal management became much more time consuming for the founders. Therefore, two organizers had to be hired to complete the union. Their work includes paying wages, implementing marketing strategies and managing vacation requests. So based on what you learned so far, how would you implement `With all the new team members on board, internal management became much more time consuming for the founders. Therefore, two organizers had to be hired to complete the union. Their work includes paying wages, implementing marketing strategies and managing vacation requests. So based on what you learned so far, how would you implement const organizers = [‘Lea’, ‘Karol’, ‘Ricarda’]?

If you don’t know the answer, feel free to contact us for a solution draft. We, at interfacewerk GmbH, develop high-end enterprise web applications and HMIs and consult on Angular, RxJS and UX topics. So feel free to reach out, if you would like to talk to us.

The reactive environment


One of my favorite points using reactive patterns: having independent application parts brings the code to a new dimension regarding readability. Imagine the founders did all the stuff by themselves; this could have resulted in a complete mess. Defining several divided parts of the company and transfer tasks and responsibilities makes the company work efficient and productive mantaining the quality the customer requires. For the code this means defining several parts for each observable to keep the code clean.

With RxJS and reactive programming you achieve the same as Max and Simon by sharing responsibilities and decentralising decision-making. You are able to think in building blocks and react to user actions, http responses or whatever you like to listen to. It enables the developer to make parts of the application completely independent. Remember: when we broke down the red company box to make it more detailed, we didn’t touch any of the rest of the process. This is a reason why it can make refactoring and additional feature requests much easier and quicker to implement.

Many developers forget that there must be at least one subscriber to the (cold) observable in order to make it act. A common task is to use Angular’s http.get(url) function which returns an observable (a stream of the response). But if there is no subscriber to it, it won’t process the request at all. Subscribing can happen in the form of using the observables subscribe method or in Angular preferably using its build-in async pipe. In the article we have seen the subscription process in detail. In addition to that, we have seen the observable implementation and how it can handle chaining using the pipe function.

How to continue?

As you have seen in the code snippets, there are tons of operators you can use to modify your observable. This is both a huge power but also a weakness of RxJS. With more than 100 operators, new developers are overwhelmed, confused and do not know how to start. A recommendation is to start with just a few, and to only start with nested observables (and switchMap, mergeMap, etc.) if you have understood normal ones. There is also no need to touch so-called window operators at the beginning. You can find a complete list of all operators on rxjs.dev and also there is a handy tool called decision tree available. This decision tree recommends specific operators based on your description or situation.

As mentioned in the introduction, RxJS was implemented alongside with Angular at the beginning. Angular massively uses the reactive patterns and therefore it is absolutely worth being familiar with reactive programming. Alongside those benefits, there is also a big performance impact when using RxJS correctly. You can avoid so-called change detection in many situations and only apply changes if the observable emits a new value. Especially when rendering huge lists on the DOM, RxJS is the most efficient way to handle them.

Published by Felix on 2019-10-18

Sparked your interest? Discover our services!