This is the seventh 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 look at how we generate Client- and Server-side code for multiple platforms. The approach we take is to write the majority of the code for each platform in a generic manner as a layered stack that can be extensively tested and to automatically generate a thin layer that sits on top of that stack to encapsulated the specifics of each Service.

Layered Messaging Stack

The diagram below shows the layered messaging stack that we implement for each platform. Points to note are that the stack has both Client- and Server-side components centred around the Middleware.

Layered Messaging Stack

The Client-side components are written for every platform. Whereas, the Server-side components are only written for key platforms (currently Java and .Net) that will expose Services. There is a high level of symmetry between the Client- and Server-side layers, so we can discuss both at the same time.

The lowest layer of the stack is the Middleware itself. This is then adapted to the Middleware Abstraction. On top of this are built the Publisher, Subscriber and Message implementations. These are in turn used by the Messaging Protocols of Request/Response, Request Only, Streaming, Request Stream and Publish Subscribe. Each Messaging Protocol has its corresponding Client and Server implementations.

The highest layer in the stack is the Service layer on the Server-side and the Proxy layer on the Client-side. The ServiceBase and ProxyBase components are abstract classes that encapsulate the commonality all Services and their Proxies.

On the Server-side, the concrete Service derives from ServiceBase, implements the Service Interface and is hosted by the ServiceHost. All of these components are automatically code generated.

On the Client-side, the concrete Proxy derives from ProxyBase and implements the Proxy Interface. Again, all of these components are code generated.

By adopting this architecture we are able to target any number of Client-side platforms by writing a relatively small amount of code that is readily testable.

Code Generation

In order to generate the code for each platform we use a Templating Library. We looked at numerous different libraries such as String Template and Apache Velocity. These have various advantages and disadvantages, but in the end, we settled on using FreeMarker. In our opinion, this provided the level of functionality, flexibility, ease-of-use and quality of documentation that we needed.

Templates

Some example templates are given in the following sections. It should be noted that the templates operate directly on the Service Domain Model already discussed.

Proxy Interface

The template below is for the Proxy Interface for the Web platform. It should be noted that we’re generating TypeScript code which we then transpile into JavaScript. This extra step is optional, but we find that the compile-time type checking proves invaluable in catching errors as early as possible. Here you can see that we’re creating an Interface that has a method for each operation defined in the Service Contract.

Proxy Implementation

The template below is for the Proxy Implementation for the .Net platform. Here you can see that we generate a concrete class that derives from the abstract Proxy base class and implement the Proxy Interface that was also generated. Then for each operation, we define a field of the Client type for that particular Messaging Protocol. Finally, we define methods that call onto the Proxy base class.

Message

The template below is for the Messages for the Java platform. Hopefully it’s fairly easy to read, but in essence, we create either a class that implements Serializable or and enum depending on whether it’s a complex Message or not. All of the fields, the constructor and getters etc. are generated also.

Summary

We have shown here how Code Generation can be used to transform our Service Domain Model into idiomatic code for any number of different platforms. The generated code is efficient and idiomatic to each platform. By adopting this strategy, we are able to rapidly target new platforms by writing a relatively small set of components.

The next post can be found here.