Architecture and system design is involving rapidity in the last years. We are talking about REST Services, microservices architectures, and reactive programming. The last one is sometimes a kind of a mystery because it often sounds like it is the holy grail in modern software design. A good overview about this programming paradigm and its difference to other concepts can be read in David Bushmans article.
Reactive programming is an important architecture style but it can bring also a lot of new complexity in your application design. Let's explain me this in an example:
Imagine the following software design: Our company is selling products not only online but also via service agents. On the one side we have a business application which is managing all the orders. After a new order is processed by the service agent, first an invoice needs to be sent to the customer. After that our company ships the product to the customer. For the invoice process we want to use a cloud service which is developed by another developer team. So let's think about how the order and invoice process can be handled by our business application. No doubt, we want to use a RESTful service interface between our business application and the invoice cloud service. We can design our solution with the following rest service based interaction between the two services:
The invoice service provides a REST service interface where we can post a new order. All we have to do is to send the order data to the service. If we receive an HTTP response 200 we know the invoice was successfully processed and we can ship our product. After the customer has paid the invoice the cloud service will do the same in the other way. Our business application offers a REST service where the invoice service can post the payment data. We respond with an HTTP 200 to signal the invoice service that we have received the payment information and updated the order status. So finally the service agent can verify and close the order.
This all works fine and, after all, this is no bad software design. But we did not make use the reactive programming paradigm here. This means we have all the bad stuff of synchronous and IO blocking architecture. Your service calls are implemented by a model called “one-request-per-thread” and those threads can spend a significant amount of time in “IO Waiting” states due to blocking I/O calls and not doing work. You can see this in the sequence diagram above. So our service agent can maybe have a bad performance when creating a new order.
How can we change the design into a non-blocking reactive style, which is often called as faster, better, cheaper? Again we are convinced in our RESTful service interfaces. But now we decouple both systems better to get faster response times.
What you can see here is a different solution for the same problem. Now the invoice service still provides a REST service interface, but this time we are just sending the order data in a non-blocking style. The invoice service handles the creation of the invoice asynchronously. This means our first request is now much faster and is not waiting until the invoice is processed by the invoice service. The invoice service can scale much better and our business application is not blocked. After the invoice was processed by the invoice service, the service calls our business application and sends the invoice number for the order request. This also did not block our invoice service because we just accept the invoice number to be stored in a queue. Our business application can check the data of the invoice asynchronous to see if the invoice was successfully created and sent to the customer. If so our application can update the order status. The service agent can check the status of the order to see if he can ship the product.
Finally, when the customer has paid the invoice, we will do implementing the same concept for processing the payment data. The invoice service just sends a payment event to the business application. The business application can process this event asynchronously and can request the payment data from the invoice service without blocking other threads. So the service agent can check the payment status and can finally close the order in our business application.
Is It Better, Faster, Cheaper?
We have decoupled our systems much better and followed the reactive programming paradigm. But what does this mean for our business case as a whole? Remember—in our example the order was processed by a service agent 'manually.' Our company doesn't want to ship products without sending an invoice first. This means when our service agent is processing the orders he now will only receive a message from the invoice service that the invoice was sent to be processed in the other system. This means our service agent can not ship the products yet. He has to stop his work and check the order status again after some period of time. And this will change the whole organization of the business process. Users still can't wait! So what will happen is that our user will change the way he is processing the customer's orders. One day he will enter only the order data into the system of all new orders. The next day he will check all the orders from the day before and verify if the invoice was sent so he can ship the products. You can see that now our IT systems performs much better, but our customer has to wait one day longer to receive our product. The same use case can happen with the payment process. When the customer has paid and calls our company to ask if the payment was received, our service agent can again not answer immediately. He can only tell the customer that a payment information was sent, but he can not check this in time, because our business application was maybe still not able to receive the payment data and update the order information. Our customer will not be very happy at all with our service.
I had to overstate slightly the scenario to point out that the response time is not the only criteria for good software design. It is important to verify whether a synchronized status of a business object is more important to the business case than the response time of an IT system. Reactive software design is important for decoupled systems with massive automated business processes and can solve a lot of performance issues. But this is not always the scenario your application has to deal with.
So you should carefully think about when to use the reactive programming style and when it is a better choice to accept a “one-request-per-thread” blocking system. In any case it is important to understand the concepts of a shiny new architecture before starting with your application design.