Reactive, Service Oriented Architecture

This is the fourth in the sequence of blog posts regarding some of experiences in creating Trading and eCommerce platforms at Adaptive. The previous post can be found here.

In this post, we present a Reactive variant of Service Oriented Architecture.

We have found that Service Oriented Architecture (SOA) is a very useful abstraction for building cross-platform, distributed systems. Cross-platform SOA in and of itself is not a new concept. There have been many implementations over the years, most notably CORBA. However, we find invariably implementations couple SOA with Remote Procedure Calls (RPC).

We have found that the good things about SOA are that a Service makes for a natural unit of deployment and, therefore, versioning. It's also useful to group related Operations into logical Services. We can also reason about services for dynamic discovery and failover.

The Problem with RPC

The problems with RPC are manifold. Firstly, we find that the request-response paradigm only addresses a subset of our use-cases. For instance, it is useful for interactions like getting reference data or executing a Trade request. However, it is not a good fit for streaming executable Prices or requesting for a Quote. To encapsulate these flows using RPC would result in polling for new data, which completely defeats the purpose of using MOM.

Secondly, that majority of SOA frameworks implement RPC synchronously. This is a very bad thing as it gives a false impression of instantaneous response and reliability to a system that is fundamentally laggy and unreliable. This encourages developers omit correct error handling and chain together RPC calls that will block threads that lead to performance problems.

Go Reactive!

We have found that a much better approach to SOA is to follow a Reactive methodology. Meaning, to represent Streams as first-class concepts and to embrace asynchrony in our APIs. At Adaptive, we have extensive experience of using Reactive Extensions (Rx).

To summarise what Streams look like in Rx, here's some pseudocode of the interfaces:

Observable is an interface that represents a Stream. It is a generic interface and can be parameterized for any payload. There is a single method to subscribe to the stream.

    interface Observable {
        Subscription subscribe(Observer observer);

Observer is a callback interface that has three methods. These are onNext for when a new update occurs, onCompleted for when there is no more data and onError if the stream fails for whatever reason.

    interface Observer {
        void onNext(T next);
        void onCompleted();
        void onError(Exception error);

Subscription is a generic interface for cleaning up resources. This is achieved by calling the parameterless unsubscribe method, which will result in the subscription to the stream being closed.

    interface Subscription {
        void unsubscribe();

So, we can see that with three very simple interfaces, we can encapsulate any number of complex interactions. In actuality, we tend not to use the actual Rx libraries in our Reactive SOA implementations. The reasons for this being that Rx is not available for all of the platforms that we target, we don't want to enforce a dependency on our clients and for interactions like request response, in environments where every message is a precious resource, it would be wasteful to have to send an onNext and an onCompleted message. Instead, we end up going with a set of interfaces that are greatly influenced by Rx, but optimise for key messaging protocols.

Cross Platform Tooling

When defining Services that span a number of different platforms, it's clear that we cannot define the Service Contracts in the language of any one of those platforms. For this reason, we define our Services in a Domain Specific Language (DSL). This gives us an abstract definition that we can then transform to idiomatic representations for each platform of interest.

The process of transforming this Service Definition into Client and Server code is summarised in the diagram below:

Cross Platform Tooling

The transformation from DSL to Client and Server code has a number of stages including Lexing which transforms the stream of characters into lexical tokens, Parsing to transform these tokens into a parse tree, Modelling to project the parse tree into a domain model and, finally, code generation to realise the model in Client- and Server-side code.

The next post can be found here.



John Marks

Director at Adaptive Financial Consulting

Domain Specific Languages


Contact us

By pressing "Send" I agree that I am happy to be contacted by Adaptive Financial Consulting. I can unsubscribe at any time. You can read more about our privacy policy here.