View on GitHub

MDSL

A domain-specific language to specify (micro-)service contracts and their data representations (realizing the API Description pattern from MAP)

MDSL Tutorial

Note: Work in progress - this tutorial currently is in a draft status, subject to change at any time!

Getting Started

Let us assume we want to create an HTTP resource API that supports upload and download of spreadsheets. The structure of these sheets resembles that of the popular CSV format and supporting tools. We choose a contract-first approach to design this API.1

Unlike Swagger/Open API Specification, Microservice Domain-Specific Language (MDSL) is a technology-neutral notation; see MDSL home page for more positioning information.

Modeling Representation Elements

Let’s start with data modeling. A spreadsheet may contain several sheet tabs (sometime also called worksheets). This multiplicity can be indicated by an asterisk *:

data type CSVSpreadsheet CSVSheetTab*

The content of such sheet tabs is usually structured into rows, and the tabs usually also have unique names. Hence, we model them as Parameter Trees, a pattern from the MAP language. In MDSL, parameter trees are represented by curly braces {}:

data type CSVSheetTab {"name": V<string>, "content": Rows*}

"name": V<string>is a fully specified Atomic Parameter (scalar). "content": Rows* is the second parameter, referencing yet another data type definition. Each sheet may have an arbitrary number of rows (indicated by an asterisk *).

Each row is identified by a line number and features data in at least one column (which is indicated by a plus +):

data type Rows {"line": ID<int>, "columns":Column+} 

This atomic parameter is not characterized as a plain value V as the name in CSVSheetTab above, but as a unique identifier ID. Identifier Element is another MAP leveraged by and integrated into MDSL. 2

Columns are often identified by characters position and, optionally, a more expressive text header (optionality is indicated by the ? modifier in MDSL):

data type Column {"position": ID<string>, 
                  "header": V<string>?, 
                  "cell": Cell}

The combination of columns and rows gives us the powerful matrix/cell structure that we would expect from spreadsheets. We have now arrived at the cell level. Cells either contain text, numbers or formulas (in this example):3

data type Cell { "text":V<string> 
               | "integerValue": V<int> 
               | "longValue": V<long> 
               | "formula": V<string> }

Modeling Operations and Endpoints

We are now ready to send instances of the complex (nested, repetitive) CSVSpreadsheet over the wire; it can serve as a Data Transfer Object (DTO).4 We need a service contract to do that.

Operation specifications in MDSL are quite talkative (unlike the rather compact data type definitions we have worked with so far):

  operation downloadSpreadsheet
    expecting payload ID
    delivering payload CSVSpreadsheet
      reporting error "SheetNotFound" 

The request and response messages are defined via their data type structures. Optionally, error reporting information can be added (which comes in the form of a parameter specification; see “Reporting” section on the quick reference page for an example). In the above example (downloadCSVFile), the request is a simple ID(entifier) that is not specified any further at this point; the response is the CSVSpreadsheet DTO from above. One error message is defined, reporting that the received ID does not match any existing spreadsheet.

To complete the example, we should also model an upload operation:

  operation uploadSpreadsheet
    expecting payload CSVSpreadsheet
    delivering payload {"successFlag":V<bool>, ID}

The two operation specifications appear inside an endpoint specification (which corresponds to a resource definition in REST):

endpoint type SpreadSheetExchangeEndpoint
exposes 
  [operations go here]

Wrapping it all up (Full Specification)

We are still missing an API description wrapper and, optionally, can add a sample client and provider:

API description SpreadSheetExchangeAPI

data type CSVSpreadsheet CSVSheetTab*
data type CSVSheetTab {"name": V<string>, 
                       "content": Rows*}
data type Rows {"line": ID<int>, 
                "columns":Column+}
data type Column {"position": ID<string>, 
                  "header": V<string>?, 
                  <<Entity>> "cell": Cell}
data type Cell {"formula":V<string> 
               | "intValue": V<int> 
               | "longValue": V<long> 
               | "text": V<string>}

endpoint type SpreadSheetExchangeEndpoint
exposes 
  operation uploadSpreadsheet
    expecting payload CSVSpreadsheet
    delivering payload {"successFlag":V<bool>, ID}
    
  operation downloadSpreadsheet
    expecting payload ID
    delivering payload CSVSpreadsheet
      reporting error "SheetNotFound" 

API provider SpreadSheetExchangeAPIProvider
offers SpreadSheetExchangeEndpoint

API client SpreadSheetExchangeAPIClient
consumes SpreadSheetExchangeEndpoint

We are done modeling and would be ready to implement the contract and deploy a provider supporting it. An intermediate step would be to create a platform- and technology-specific contract such as a Swagger/Open API Specification.

Bonus/Outlook: MAP Decorators

MDSL is aware of the Microservice API Patterns; these patterns can be used to annotate endpoints, operations, and representation elements. This makes the machine-readable specification more expressive (in comparison to formated comments or free-form texts accompanying the formal specification):

API description SpreadSheetExchangeAPI

data type CSVSpreadsheet CSVSheetTab*
data type CSVSheetTab {"name": V<string>, 
                       "content": Rows*}
data type Rows {"line": ID<int>, 
                "columns":Column+}
data type Column {"position": ID<string>, 
                  "header": V<string>?, 
                  <<Entity>> "cell": Cell}
data type Cell {"formula":V<string> 
               | "intValue": V<int> 
               | "longValue": V<long> 
               | "text": V<string>}

endpoint type SpreadSheetExchangeEndpoint serves as CONNECTOR_RESOURCE
exposes 
  operation uploadSpreadheet with responsibility EVENT_PROCESSOR
    expecting payload CSVSpreadsheet
    delivering payload {"successFlag":V<bool>, ID}
    
  operation downloadCSVFile with responsibility RETRIEVAL_OPERATION
    expecting payload ID 
    delivering payload CSVSpreadsheet
      reporting error "SheetNotFound" 

API provider SpreadSheetExchangeAPIProvider
offers SpreadSheetExchangeEndpoint

API client SpreadSheetExchangeAPIClient
consumes SpreadSheetExchangeEndpoint

This enhanced specification states that SpreadSheetExchangeEndpoint serves as a CONNECTOR_RESOURCE; uploadCSVFile is an EVENT_PROCESSOR and downloadCSVFile is a RETRIEVAL_OPERATION. The cell content in the Column data type is an <<Entity>>. All these decorators are defined as patterns in the MAP language.

Copyright: Olaf Zimmermann, 2019. All rights reserved.

  1. There are two ways to approach API design, code first and contract first. With code first, one writes an API implementation, annotates the API with the required routing and (de)serialization information and lets tools and middleware generate a contract. In contract first, one authors an interface description, for instance in Swagger/Open API Specification and then (optionally) lets tools generate client and server stubs. MDSL supports contract-first service design at present. 

  2. Other element stereotypes, defined in MAP, are Metadata MD and Link L.  

  3. ignoring special types of cells such as pivot tables and data visualizations, as supported for instance in Microsoft Excel, in/for this simplified example. 

  4. In our integration context, Data Transfer Representation (DTR) is a better name, actually.