Appendix C

Microservice Domain-Specific Language (MDSL)

This appendix introduces as much of the Microservice Domain-Specific Language (MDSL) as required to understand the examples in Parts 1 and 2 of the book. MDSL is applicable irrespective of any architectural styles and supporting tech-nologies; hence, MDSL may stand for Message and Data Specification Language as well.

MDSL allows API designers to specify API contracts, their data representations, and bindings to technologies. The language supports the domain model and patterns from this book in its syntax and semantics. Its tools provide generators for interface description and service programming languages such as OpenAPI, gRPC protocol buffers, GraphQL, Application-Level Profile Semantics (ALPS), and Jolie (which also brings support for Web Services Description Language [WSDL] and XML Schema conversion).

The MDSL language specification and supporting tools are available online.1

Getting Started with MDSL

First and foremost, MDSL supports the API DESCRIPTION pattern from Chapter 9. To specify such API contracts, MDSL picks up domain model concepts such as API endpoint, operation, client, and provider, as introduced in Chapter 1.

The patterns from this book are integrated into the language natively. For instance, ATOMIC PARAMETERS and PARAMETER TREES from Chapter 4 structure the data definitions. Moreover, the roles and responsibilities introduced in Chapter 5 can be assigned to endpoints and operations. On the message representation level, MDSL includes decorators for the element stereotypes and quality patterns from Chapters 6, 7, and 9; an example is <<Pagination>>. Finally, the evolution patterns from Chapter 8 are integrated into the language as well: API providers and their SERVICE LEVEL AGREEMENTS may disclose lifecycle guarantees such as EXPERIMENTAL PREVIEW or LIMITED LIFETIME GUARANTEE; many language elements may receive a VERSION IDENTIFIER.

1. https://microservice-api-patterns.github.io/MDSL-Specification.

Design Goals

As a contract language for service and API design, MDSL aims at facilitating agile modeling practices, API sketching, and API design workshops. It is supposed to be readable for all stakeholder groups involved in API design and evolution. MDSL should support partial specifications that can be refined iteratively. To be usable in tutorials and publications such as this book, its syntax must be compact so that a nontrivial API contract still fits on one book page or presentation slide (or less).

MDSL can be used in top-down API design from requirements (for instance, user stories for integration scenarios and API sketches) down to code and deployment artifacts and in bottom-up discovery of internal interfaces in existing systems, possibly wrapped in public, community, or solution-internal remote APIs. One example of a top-down design process is Align-Define-Design-Refine (ADDR) [Higginbotham 2021], as introduced at the beginning of Part 2; a discovery tool with MDSL support is the domain-driven Context Mapper [Kapferer 2021].

MDSL aims at platform independence of API designs. API DESCRIPTIONS created with MDSL are not limited to HTTP or any other single protocol or message exchange format. The HTTP protocol design deviates from most interface definition languages and RPC-style communication protocols in many ways. Hence, MDSL has to provide configurable provider-to-technology bindings that surmount protocol differences—without losing either generality or specificity.

“Hello World” (API Edition)

The “Hello World” of MDSL and service API design look as follows:

API description HelloWorldAPI

data type SampleDTO {ID<int>, "someData": D<string>}

endpoint type HelloWorldEndpoint
exposes
  operation sayHello 
  expecting payload "in": D<string>
  delivering payload SampleDTO

API provider HelloWorldAPIProvider
  offers HelloWorldEndpoint
  at endpoint location "http://localhost:8000"
  via protocol HTTP
    binding resource HomeResource at "/"
      operation sayHello to POST

There is a single endpoint type, HelloWorldEndpoint, that exposes an operation sayHello. This operation has a single inlined request parameter, "in": D<string>, and returns an unnamed data transfer object (DTO) called SampleDTO as output. This DTO is modeled explicitly so that its specification can be reused. The sample DTO is a PARAMETER TREE, {ID<int>, "someData": D<string>}, which is flat in this case. The DATA ELEMENT D in the tree is called "someData" and has a string type; the unnamed ID parameter, an ID ELEMENT, is of type integer.

Each endpoint type describes a platform-independent API contract, possibly provided multiple times. In addition to the endpoint type HelloWorldEndpoint, the example contains an API provider instance HelloWorldAPIProvider exposing an API implementation that binds the abstract endpoint type to HTTP. The endpoint type is bound to a single HTTP resource HomeResource in the example. The single endpoint operation sayHello is bound to the POST method in this single resource. The resource URI is assembled from two parts marked with the keyword at, one on the endpoint level and one on the resource level. The request parameters could be bound individually and explicitly to QUERY or PATH or other parameter types defined in the HTTP RFCs; this is not shown in the example, and a default binding to the request BODY is assumed.

It is possible to specify data types preliminarily:

data type SampleDTOStub {ID, "justAName"}

SampleDTOStub is specified incompletely. The first element in this flat PARAMETER TREE has an identifier role ID, but it does not have a name yet and its type is unknown still. The role and type of the second parameter have not been specified either; it merely consists of "justAName". Such incomplete, preliminary specifications are useful when only sketching interfaces at early design stages or when the details in a given modeling context are not of concern.

The online “Primer: Getting Started with MDSL”2 and the project repository provide additional examples.

2. https://microservice-api-patterns.github.io/MDSL-Specification/primer.

MDSL Reference

Let us now walk through the language concepts in depth.

API Endpoint Types (with Message Specifications)

The grammar of MDSL is inspired by the domain model from Chapter 1. An API DESCRIPTION in MDSL features one or more endpoint types, which expose operations. These operations expect and deliver request and/or response messages. The request and response messages sent and received by operations consist of simple or structured data. A comprehensive example looks as follows:

API description CustomerRelationshipManagementExample

endpoint type CustomerRelationshipManager
  serves as PROCESSING_RESOURCE
data type Customer P
exposes
  operation createCustomer
    with responsibility STATE_CREATION_OPERATION
    expecting payload "customerRecord": Customer
    delivering payload "customerId": D<int>
    compensated by deleteCustomer
  // no GET operation yet
  operation upgradeCustomer
    with responsibility STATE_TRANSITION_OPERATION
    expecting payload "promotionCode": P // partially specified
    delivering payload P // response unspecified
  operation deleteCustomer
    with responsibility STATE_DELETION_OPERATION
    expecting payload "customerId": D<int>
    delivering payload "success": MD<bool>
    transitions from "customerIsActive" to "customerIsArchived"
  operation validateCustomerRecord
    with responsibility COMPUTATION_FUNCTION
    expecting
      headers "complianceLevel": MD<int>
      payload "customerRecord": Customer
    delivering
      payload "isCompleteAndSound": D<bool>
    reporting
      error ValidationResultsReport
        "issues": {"code":D<int>, "message":D<string>}+

The CustomerRelationshipManager API exposes and serves as a PROCESSING_RESOURCE (or, following our layout convention for pattern names, as a PROCESSING RESOURCE). This is one of the responsibility patterns in Chapter 5. Its four operations differ in their read-write characteristics; this is expressed by the with responsibility decorators. For instance, upgradeCustomer is a STATE_TRANSITION_OPERATION. All operations in this example have request and response messages (which is not mandatory in MDSL, as message exchange patterns may vary). The header and payload content of request and response messages is modeled via MDSL data transfer representations, introduced in the section “Data Types and Data Contracts” that follows.

Some of the operations define undo operations (compensated by) and state transitions (transitions from ... to). The operation validateCustomer-Record can return an ERROR REPORT, which is a pattern from Chapter 6. Note that one or more issues can be reported in ValidationResultsReport due to its “at least one” cardinality, indicted with the plus symbol, "+". This operation also features a request header, "complianceLevel": MD<int>, which has a metadata role and an integer type.

See “Service Endpoint Contracts in MDSL” in the online language specification for more explanations [Zimmermann 2022].

Data Types and Data Contracts

Throughout the book, we emphasized the importance of data modeling; the Published Language of an API contains data representations in several places, which may be flat or nested:

  • Endpoint types define operations, which have request and (optionally) response messages containing payload content and metadata headers. The structure of these messages has to be specified unambiguously and agreed upon to achieve interoperability as well as accuracy, and it must ensure a positive client developer experience.

  • When certain data structures are used by multiple operations, these operations may refer to shared data transfer representations (which are message-level pendants to DTOs internal to programs). Such representations become part of one or more API endpoint contracts.

  • APIs may be used to emit and receive events. Such events also require data definitions.

API data definitions have a strong impact on the success of an API because the amount of coupling between client and provider is influenced by these definitions. MDSL supports data modeling in several ways, addressing the preceding usage scenarios. The MDSL data types are inspired—and generalize from—message exchange formats such as JSON. Here are two examples:

data type SampleDTO {ID, D<string>}

data type Customer {
  "name": D<string>,
  "address": {"street": D<string>, "city": D<string>}*,
  "birthday": D<string> }

The basic structure patterns from Chapter 4, ATOMIC PARAMETER and PARAMETER TREE in particular, provide the type system of MDSL. In the example, "name": D<string> is an ATOMIC PARAMETER, and Customer is a nested PARAMETER TREE, containing an inner PARAMETER TREE representing one or more "address" ele-ments. This is indicated by the asterisk * at the end of its definition.

Parameter Trees and Forests

Nesting is supported to realize the PARAMETER TREE pattern. The structure is expressed in an object- or block-like syntax: {...{...}}. This syntax is similar to objects in data representation languages such as JSON.

The preceding example features two trees; one of these trees is inlined in the message specification:

"address": {"street": D<string>, "city": D<string>}

Usage of the PARAMETER FOREST pattern is indicated by square brackets [...]:

data type CustomerProductForest [
  "customers": { "customer": CustomerWithAddressAndMoveHistory}*;
  "products": { "product": ID<string> }
]
Atomic Parameters (Full or Partial Specification)

Full ATOMIC PARAMETERS are defined as Identifier-Role-Type triples: "aName":D<String>

  • The optional identifier "aName" corresponds to the variable names in programming languages and data representation languages such as JSON. Identifiers must be embedded in double quotes: "somePayloadData". They may contain blanks: " " or underscores "_".

  • The mandatory role can be D (data), MD (metadata), ID (identifier), or L (link). These roles directly match the four element stereotype patterns from Chapter 6: DATA ELEMENT, METADATA ELEMENT, ID ELEMENT, and LINK ELEMENT.

  • The base types are bool, int, long, double, string, raw, and void. This type information is optional.

For instance, D<int> is an integer data value and D<void> is an empty representation element.

Rationale for Identifier-Role-Type Triple Concept

The primary specification element is the role within a message payload taken by a particular part of a header or payload (or a representation element in our domain model terminology); identifiers and data type are optional. Making identifier and type optional supports the early use of MDSL when an API design is not yet complete:

operation createCustomer
  expecting payload "customer": D
  delivering payload MD

The three-part specification is a bit different from the identifier-type pairs typically used in programming languages. As stated earlier, only the role is mandatory. This makes it possible to create rather compact specifications during agile API modeling. An abstract, unspecified element can be represented as P (for parameter or payload placeholder). P may take the place of the Role-Type elements in the Identifier-Role-Type triple; the placeholder can also replace the entire triple:

operation upgradeCustomer
  expecting payload "promotionCode": P // placeholder
  delivering payload P // response unspecified

A "nameOnly" may also specify a generic placeholder parameter (which has no role and no type).

Multiplicity

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

The online language reference provides more explanations under “Data Contracts and Schemas in MDSL” [Zimmermann 2022].

Providers and Protocol Bindings (HTTP, Other Technologies)

MDSL by design generalizes and abstracts from concepts in other API contract languages. This is rather straightforward for most of them (and has been done before in other interface definition languages). For HTTP resource APIs, additional concepts and an intermediate step are required because MDSL endpoints do not map to resources and their URIs one-to-one. In particular, dynamic endpoint addressing, as promoted by RFC6570 “URI Templates” [Fielding 2012] and used in HTTP path parameters, is specific to HTTP. Moreover, it is not obvious how to express complex request payloads of retrieval operations—HTTP GET and request bodies do not go well together.3 HTTP also handles addressing, request and response parameters, errors, and security concerns in specific ways (for good reasons).

The missing mapping information can be specified in an explicit provider-level HTTP binding:

API provider CustomerRelationshipManagerProvider version "1.0"
offers CustomerRelationshipManager
  at endpoint location "http://localhost:8080"
via protocol HTTP binding
  resource CustomerRelationshipManagerHome
    at "/customerRelationshipManagerHome/{customerId}"
    operation createCustomer to PUT // POST taken
      element "customerRecord" realized as BODY parameter
    // no GET yet
    operation upgradeCustomer to PATCH
      element "promotionCode" realized as BODY parameter
    operation deleteCustomer to DELETE
      element "customerId" realized as PATH parameter
    operation validateCustomerRecord to POST
      element "customerRecord" realized as BODY parameter
provider governance TWO_IN_PRODUCTION

To generate OpenAPI and, later on, server-side stubs and client-side proxies from MDSL specifications, not all of the required binding information can be derived from the abstract endpoint types. A particularly important example is the mapping of MDSL operations to HTTP verbs such as GET, POST, PUT, and so on. Hence, additional mapping details, for instance, whether a payload parameter is transferred in the QUERY string or the message BODY (or the URI PATH or a HEADER or a COOKIE), can also be provided (as motivated earlier). Error reports and security policies can also be bound, and media type information can be provided.

3. The protocol specifications are not fully explicit and precise here; that said, many tools and protocol runtimes do not support this combination.

See “Protocol Bindings for HTTP, gRPC, Jolie, Java” in the online language specification for more explanations [Zimmermann 2022].

Summary of Support for Microservice API Patterns

MDSL supports the Microservice API Patterns (MAP) presented in this book in several ways:

  1. The basic representation elements serve as MDSL grammar rules in the data contract part. PARAMETER TREE and ATOMIC PARAMETER are the main constructs; ATOMIC PARAMETER LISTS and PARAMETER FORESTS are supported as well. PARAMETER TREES correspond to JSON objects {...}; the set cardinalities "*" and "+" indicate that an API that uses JSON as its message exchange format should send or receive a JSON array [...]. Base types such as int, string, and bool can also be found in MDSL.

  2. Foundation patterns may appear as decorator annotations for the entire API description, for instance, PUBLIC_API and FRONTEND_INTEGRATION. The other visibility and direction patterns from Chapter 4 are supported as well: PUBLIC_API, COMMUNITY_API, SOLUTION_INTERNAL_API.

  3. Role and responsibility decorators exist on endpoint and operation levels. Some patterns serve as decorators on the API endpoint level, for example, expressing PROCESSING_RESOURCE and MASTER_DATA_HOLDER roles. Other responsibility patterns appear as decorators representing operation responsibilities, for instance, COMPUTATION_FUNCTION and RETRIEVAL_OPERATION (Chapter 5).

  4. Representation element stereotypes provide the options for the role part of the Identifier-Role-Type triples defining ATOMIC PARAMETERS: D (data), MD (metadata), L (link), and ID (identifier).

  5. Explicit data types and inlined representation elements can be annotated with pattern decorators as well. Examples of stereotypes decorating representation elements are <<Context Representation>> and <<Error_Report>> from Chapter 6 as well as <<Embedded_Entity>> and <<Wish_List>> from Chapter 7.

The following elaborate example features all five types of MAP support in MDSL:

API description CustomerManagementExample version "1.0.1"
usage context SOLUTION_INTERNAL_API
  for FRONTEND_INTEGRATION

data type Customer <<Data_Element>> {ID, D} // preliminary

endpoint type CustomerRelationshipManager
  serves as INFORMATION_HOLDER_RESOURCE
  exposes
    operation findAll with responsibility RETRIEVAL_OPERATION
      expecting payload "query": {
        "queryFilter":MD<string>*,
        "limit":MD<int>,
        "offset":MD<int> }
      delivering payload
        <<Pagination>> "result": {
          "responseDTR":Customer*,
          "offset-out":MD<int>,
          "limit-out":MD<int>,
          "size":MD<int>,
          "self":Link<string>,
          "next":L<string> }*

The PAGINATION pattern (Chapter 7) usage is indicated in the findAll opera-tion; the message design in the specification of CustomerRelationshipManager follows the solution sketch in the pattern with pattern-specific representation elements such as "limit". In line with the pattern description, the client specifies a "limit" and an "offset" in offset-based pagination.

The example contains instances of all four types of element stereotypes such as METADATA ELEMENT and LINK ELEMENT. Both long and short names may specify data elements; both options are used in the example (see "self" and "next"):

  • Data or D, representing a plain/basic data/value role. D corresponds to DATA ELEMENT.

  • Identifier or ID for identifiers, corresponding to the pattern ID ELEMENT.

  • Link or L for identifiers that are also network-accessible (as, for instance, URIs links are) as described in the LINK ELEMENT pattern.

  • Metadata or MD representing control, provenance, or aggregate METADATA ELEMENTS.

The element role stereotypes can be combined with the base types to yield precise specifications of ATOMIC PARAMETERS.

Using the MDSL decorator annotations and stereotypes is optional. If present, they make the API description more expressive and can be processed by tools such as API linters/contract validators, code/configuration generators, MDSL to OpenAPI converters, and so on.

MDSL Tools

An Eclipse-based editor and API linter, also offering transformations for rapid, goal-driven API design (“API first”) and refactoring to many of the patterns from this book is available. Not only can MDSL specifications be validated, but platform-specific contracts (OpenAPI, gRPC, GraphQL, and Jolie) can be generated. A prototypical MDSL-Web4 tool is available as an open-source project. A command-line interface (CLI) offers most of the IDE functionality; therefore, it is not imperative to work with Eclipse when creating and using MDSL specifications.

An intermediate generator model and API exist so that support for other target languages and integration with other tools can be added. Template-based reporting is available via Apache Freemarker. One of the available sample templates turns the MDSL into Markdown.

See “MDSL Tools: Users Guide” for updates [Zimmermann 2022].

Online Resources

The definite, up-to-date language reference can be found online.5 An MDSL primer, a tutorial, and a quick reference are available on the MDSL Web site as well.

The Interface Refactoring Catalog introduced in Chapter 11 specifies many of its API refactorings with before-after MDSL snippets:

https://interface-refactoring.github.io

Step-by-step instructions and demos of MDSL Tools are available as blog posts:

https://ozimmer.ch/categories/#Practices

4. https://github.com/Microservice-API-Patterns/MDSL-Web.

5. https://microservice-api-patterns.github.io/MDSL-Specification.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.87.196