Domain Specific Languages

This is the fifth 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.

This post discusses the a Domain Specific Language (DSL) for the definition of Reactive SOA Service Contracts.

DSLs for Service definition are not a new thing. In fact, CORBA's IDL is a very good example. However, our requirements are somewhat different as we need to declare operations according to a set of messaging protocols. For this reason, we have created our own DSL.

Example DSL

The following sections define this DSL in terms of the entities that are defined. For each entity there is a summary of the use-case and a description.

Namespaces

When we generate code for various different platforms, each of those platforms has its own idiom for namespacing the generated code. In the case of Java this is a package name consisting of a nested set of identifiers in lower case separated by dots and prefixed with the reversed domain of the originating organisation. In .Net, the namespace is generally camel cased and dot separated. In JavaScript, generated objects and functions should be grouped under an object in order not to pollute the global namespace. In C++, namespaces are generally lower-case and excessive nesting is discouraged.

For these reasons, we define the namespaces for each target platform in the DSL.


    namespaces {
            java com.weareadaptive.messaging.sample;
        html Sample;
        dotnet WeAreAdaptive.Messaging.Sample;
        cpp sample
    }

Messages

Next we define the Messages to be exchanged by the Service. These message definitions can be arbitrarily complex and nested and can encompass enumerations and primitive types. Individual fields can be declared as being required or optional. This is of particular importance for version tolerance, where it is important to have optional fields to provide an upgrade path between versions.


    messages {
            message SampleRequest {
                required string input;
            }
        
        enum SampleResponseType {
            SUCCESS,
            FAILURE
        }

            message SampleResponse {
                required SampleResponseType type;
            optional string output;
            }

            message SampleUpdate {
                required int value;
                required string text;
            }
        }

Services

Finally, we define a number of Services. Each Service has a unique name and any number of operations.


    services {

        service Sample {
            REQUEST_RESPONSE sampleRequestResponse(SampleRequest) yields SampleResponse;
            REQUEST_ONLY sampleRequestOnly(SampleRequest);
            STREAM sampleStream yields SampleUpdate;
            REQUEST_STREAM sampleRequestStream(SampleRequest) yields SampleUpdate;
            PUBLISH_SUBSCRIBE samplePublishSubscribe(SampleRequest) yields SampleUpdate;
        }
    }

Operations

Each Operation is defined by a messaging protocol, a unique name and the relevant Messages to be exchanged. Each of the messaging protocols has its own syntax maps to the interactions set out here.

Lexing and Parsing

In order to take a DSL and transform it into something useful we need to write a Lexer and a Parser. Lexing involves taking a stream of characters and grouping them into lexical tokens. Parsing is the process of taking a stream of these tokens and transforming them into a Parse Tree that conforms to syntactic structure of the DSLs grammar.

There are many ways to write Lexers and Parsers. For the masochistic, this can be done by hand. For the rest of us, tools exist to generate them automatically. Traditionally, tools like Lex and Yacc and their GNU equivalents (FLEX and Bison) are used. We tend to use higher-level tools like the popular, Java-based parser generator ANTLR. We have found this to be easy to use and very flexible, whilst implementing very efficient parsers.

Summary

In this post we have discussed how a DSL can be defined to specify reactive Service contracts for multiple platforms. This DSL is realised in a grammar that can be transformed into a Lexer and Parser using tooling. The output of this process is a Parse Tree that can then be further transformed.

The next post can be found here.

 

John Marks

CCO and co-founder,
Adaptive Financial Consulting



×

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.