View on GitHub

Microservice DSL (MDSL)

A Domain-Specific Language (DSL) to specify (micro-)service contracts, their data representations and API endpoints.

Data Contracts and Schemas in MDSL

Use Cases for MDSL Data Type Models

Concepts

The structure patterns from MAP form the base of the type system that is used in MDSL. Identifier-Role-Type (IRT) triples "aName":D<String> for Atomic Parameters (only the role is mandatory):

Simple, yet powerful nesting is supported (as known from data representation languages such as JSON):

Already existing metamodels and schema languages can be used alternatively to MAP_TYPES. Examples are: JSON_SCHEMA, XML_SCHEMA, and PROTOCOL_BUFFER.

MDSL specifications do not have to be complete to be useful (e.g., in early stages of service design); tools are expected to check completeness, use defaults for missing parts, etc.

The I in IRT: Identifiers

Identifiers must be embedded in double quotes. They may contain blanks or underscores.

The R in IRT: Role stereotypes for representation elements/data types

The role within a message payload that is played/taken by a particular part of a header or payload (a representation element in MAP terminology) is the primary specification element; identifiers and data type are optional. This three-part specification (with only one mandatory part) is quite different from the identifier-type pairs typically used in programming languages. It makes it possible to create rather compact (but still incomplete) specifications during agile API modeling.

An abstract, unspecified element is represented as P (for parameter or payload placeholder). P takes the place of the Role-Type elements in the IRT triple introduced above.

Concrete atomic type refinements of P, matching the element stereotypes in MAP, are:

D<void> may represent a generic, unspecified parameter (just like P).

The T in IRT: Types

Base Types

The role stereotypes can be combined with the following base types to yield precise specifications of atomic parameters: bool, int, long, double, string, raw, and void. So D<int> is an integer data value and D<void> is an empty/non-existing payload part/parameter.

Complex Types

See explanations above (“Simple, yet powerful nesting is supported:”).

Collections and optionality

*, ?, and + turn a type definition into a collection (*: zero or more, ?: one or none, + : at least one). The default is ! (exactly one); it does not have to be modeled.

Parameter trees and atomic parameter lists can be used to express optionality if | (is used rather than ,).

Reuse of data type definitions (in multiple representation elements)

dataContract:
    'data' 'type' name=ID 
    ('version' svi=semanticVersioningIdentifier)? 
    structure=elementStructure;
    default=defaultValue?

elementStructure: 
    pf=parameterForest | pt=parameterTree 
  | apl=atomicParameterList  | np=singleParameterNode;

[...]

TreeNode:
    spn=singleParameterNode | apl=atomicParameterList | pt=parameterTree;

SingleParameterNode: 
    genP=genericParameter | atomP=atomicParameter | tr=typeReference;

The semantic versioning identifier svi is a simple STRING; at present, the entire API as well as data types, endpoints and operations can be versioned.

Default values

Only reusable, explicitly defined data types can have default values (still experimental):

data type SampleDTO {ID, D} default is "{42, 'TODO'}"

Examples

The following example features a partial specification of nested customer information as the roles/types of all “three plus three” representation elements yet unknown:

data type MoveHistory {"from", "to", "when"}  
data type CustomerWithAddressAndMoveHistory { 
    "CustomerCoreData", 
    "AddressRecords", 
    MoveHistory* // type reference
} 

Alternatively, one can start with element stereotypes and pure structure instead of element names:

data type MoveHistory {D, D, D}  // record, modeled as Parameter Tree
data type CustomerWithAddressAndMoveHistory { 
    D, 
    D, 
    MoveHistory* // type reference
} 

Once some more analysis work has been done, the specification can be refined, but still remain incomplete (as "CustomerCoreData":D does not say anything about the inner structure of the entity value):

data type AddressRecord (
    "street":D<string>, 
    "zipCode":D<int>, 
    "city":D<string>) // Atomic Parameter List in '()' syntax

data type MoveHistory 
    {"from":AddressRecord, "to":AddressRecord, "when":D<string>} 

data type CustomerWithAddressAndMoveHistory { 
    <<Entity>>"CustomerCoreData":D, 
    "AddressRecords":AddressRecord+, // one or more
    "MoveHistory": MoveHistory* // type reference, collection
} 

Note that a parameter tree that only contains atomic parameters {D<int>, D<string>} can also be modeled as an Atomic Parameter List (D<int>, D<string>). It is recommended to prefer the Parameter Tree syntax over that of the Atomic Parameter List (to promote information hiding and defer detailed modeling decisions until the last/most responsible moment).

Technology Mappings

JSON/JSON Schema

JSON MDSL Comments
Basic JSON data types Atomic Parameter Base types do not match 100%
Object (flat) Parameter Tree (flat) or Atomic Parameter List Parameter Tree preferred
Object (structured) Parameter Tree (nested) Straightforward
Array Cardinality of * or + Is homogeneous in MDSL/MAP

XML Schema

XML Schema MDSL/MAP Comments
Built-in data types Atomic Parameter Not the same expressiveness
Sequence element (referencing built-in types) Parameter Tree (flat) or Atomic Parameter List Parameter Tree preferred
Complex type Parameter Tree MDSL syntax more compact
Sequence with maxoccurs > 1 Cardinality of * or + n/a

Jolie

The MAP base types map to simple data in Jolie in a straightforward manner.

The same holds for the mapping of parameter trees to Jolie data structures.

An example can be downloaded here.

Known Limitations

Note that some combinations are syntactically possible at present (to simplify the grammar), but do not make much sense (or create ambiguity):

We expect tools (linters, model validators) to warn about inappropriate specifications.

Back to service endpoint contract types and on to optional runtime language concepts.

Quick reference.

Back to MDSL homepage.

Copyright: Olaf Zimmermann, 2018-2020. All rights reserved. See license information.