MDSL (Micro-)Service Contracts Generator
Introduction and Motivation
The Microservices Domain Specific Language (MDSL) is a Domain-Specific Language (DSL) to specify (micro-)service contracts and data representations, jointly realizing the technical part of the API Description pattern from Microservice API Patterns (MAP). MDSL, in turn, has generator tools for Open API, gRPC, GraphQL, Jolie, and plain Java.
Our MDSL generator automatically produces (micro-)service contracts out of strategic DDD context maps written in CML. The generator creates the contracts according to the following mapping, which reflects our proposal how we would derive (micro-)services from models based on strategic DDD. The generator aims at providing assistance regarding how your system can be implemented as a (micro-)service-oriented architecture.
Language Mapping
CML Input | MDSL Output | Description |
---|---|---|
Upstream Bounded Contexts from upstream-downstream relationships | Service Specification (API description) | We create one service specification for each upstream Bounded Context of your Context Map. |
Exposed Aggregates | Endpoint | Every exposed Aggregate of your upstream Bounded Context results in one endpoint. |
Application layer | Endpoint, Flow | In case you model an application layer, we generate an additional endpoint for it. The endpoint contains the operations of the application services and/or commands. Update: CML flows are mapped to MDSL flows as well; they must not contain any operations. |
Public methods/operations of the aggregate root entity or of Services. | Operation | Your exposed Aggregates should contain methods/operations, either on the aggregate root entity or in Services. For every method/operation in those objects we generate an operation in MDSL. |
Parameters & return values of methods/operations | Base types or data type specifications if possible | If you use primitive data types in CML, they are mapped to the base types of MDSL. If you refer to objects (such as entities) in CML, we produce a corresponding parameter tree. Types which are not further declared are mapped to abstract, unspecified elements (P). |
Upstream Bounded Contexts from upstream-downstream relationships | API provider | For the upstream Bounded Context we also generate an API provider. |
Downstream Bounded Contexts from upstream-downstream relationships | API client | Downstream Bounded Contexts are mapped to corresponding API clients. |
Data Type Mapping
The base/primitive types are mapped to Atomic Parameters as this:
CML type | MDSL type |
---|---|
String | D<string> |
int or Integer | D<int> |
long or Long | D<long> |
double or Double | D<double> |
boolean | D<bool> |
Blob | D<raw> |
Date | D<string> (no date available in MDSL) |
If you declare a method with multiple parameters or refer to an object (such as Entity or Value Object) in CML, we generate a corresponding Parameter Tree. For example the following entity would be mapped to the (rather flat) parameter tree below:
CML input:
Entity Address {
String street
String lockbox nullable
int postalCode
String city
}
MDSL data type result:
data type Address { "street":D<string>, "lockbox":D<string>?, "postalCode":D<int>, "city":D<string> }
All abstract data types that are not base types and not specified in CML (no references to objects) will produce an abstract,
unspecified placeholder element P
in MDSL, as the following example illustrates:
data type JustAnUnspecifiedParameterType P
Hint: Find more information in our SummerSoC 2020 paper on Domain-driven Service Design - Context Modeling, Model Refactoring and Contract Generation (authors copy).
Example
An exemplary API description in MDSL, generated by Context Mapper, is:
// Generated from DDD Context Map 'Insurance-Example_Context-Map.cml' at 21.10.2019 17:48:52 CEST.
API description CustomerManagementContextAPI
usage context PUBLIC_API for BACKEND_INTEGRATION
data type Address { "street":D<string>, "postalCode":D<int>, "city":D<string> }
data type AddressId P
data type changeCustomerParameter { "firstname":D<string>, "lastname":D<string> }
endpoint type CustomersAggregate
serves as INFORMATION_HOLDER_RESOURCE
exposes
operation createAddress
with responsibility "Creates new address for customer"
expecting
payload Address
delivering
payload AddressId
operation changeCustomer
with responsibility "Changes existing customer address"
expecting
payload changeCustomerParameter
delivering
payload D<bool>
// Generated from DDD upstream Bounded Context 'CustomerManagementContext' implementing OPEN_HOST_SERVICE (OHS) and PUBLISHED_LANGUAGE (PL).
API provider CustomerManagementContextProvider
// The customer management context is responsible for managing all the data of the insurance companies customers.
offers CustomersAggregate
at endpoint location "http://localhost:8001"
via protocol "RESTfulHTTP"
// Generated from DDD upstream Bounded Context 'CustomerManagementContext' implementing OPEN_HOST_SERVICE (OHS) and PUBLISHED_LANGUAGE (PL).
API client PolicyManagementContextClient
// This bounded context manages the contracts and policies of the customers.
consumes CustomersAggregate
API client CustomerSelfServiceContextClient
// This context represents a web application which allows the customer to login and change basic data records like the address.
consumes CustomersAggregate
IPA
Note: This example has been generated from our insurance example, which you can find in our examples repository.
Microservice API Patterns (MAP) Decorators
The MDSL language allows modelers to specify the roles of endpoints and operations according to the endpoint- and operation-level responsibility patterns in MAP. Our generators match the corresponding pattern names in comments on Aggregates and methods. The following CML code illustrates how the MAP patterns can be added in CML. In this case we use the Information Holder Resource pattern on the Aggregate level and the Retrieval Operation pattern on the method level:
BoundedContext CustomerManagementContext {
"INFORMATION_HOLDER_RESOURCE"
Aggregate Customers {
Entity Customer {
aggregateRoot
- SocialInsuranceNumber sin
String firstname
String lastname
- List<Address> addresses
"RETRIEVAL_OPERATION"
def @Address getAddress(AddressId addressId);
}
Entity Address
}
}
The patterns are detected by our generator and mapped to the corresponding language features of MDSL. The following MDSL code illustrates the resulting resource for the Aggregate specified above:
data type Address { "street":D<string>, "postalCode":D<int>, "city":D<string> }
data type AddressId P
endpoint type CustomersAggregate
serves as INFORMATION_HOLDER_RESOURCE
exposes
operation getAddress
with responsibility RETRIEVAL_OPERATION
expecting
payload AddressId
delivering
payload Address
As illustrated above, the patterns on the endpoint/resource level are added with the serves as keyword and on the operation level with the with responsibility keyword. In the following we list the supported patterns:
Endpoint Role Patterns
- PROCESSING_RESOURCE
- INFORMATION_HOLDER_RESOURCE
- OPERATIONAL_DATA_HOLDER
- MASTER_DATA_HOLDER
- REFERENCE_DATA_HOLDER
- DATA_TRANSFER_RESOURCE
- LINK_LOOKUP_RESOURCE
Operation Responsibility Patterns
Please refer to the pattern texts on the MAP website for explanations of these decorators.
Generating the MDSL Contracts
The generators can be called from the context menus of the CML editors in VS Code or Eclipse. A documentation how to call the generators can also be found here.
Note: All generator outputs will be generated into the src-gen folder.
Protected Regions
After you have generated an MDSL contract, you can add protected regions for data types, endpoint types, API providers, and API clients if you want to modify parts of the contract and protect them from being overwritten. The following example shows the corresponding comments that are required to begin and end the four different protected regions:
// Generated from DDD Context Map 'Insurance-Example_Context-Map.cml' at 21.10.2019 17:48:52 CEST.
API description CustomerManagementContextAPI
usage context PUBLIC_API for BACKEND_INTEGRATION
// ** BEGIN PROTECTED REGION for data types
// ** END PROTECTED REGION for data types
data type Address { "street":D<string>, "postalCode":D<int>, "city":D<string> }
data type AddressId P
data type changeCustomerParameter { "firstname":D<string>, "lastname":D<string> }
// ** BEGIN PROTECTED REGION for endpoint types
// ** END PROTECTED REGION for endpoint types
endpoint type CustomersAggregate
serves as INFORMATION_HOLDER_RESOURCE
exposes
operation createAddress
with responsibility "Creates new address for customer"
expecting
payload Address
delivering
payload AddressId
operation changeCustomer
with responsibility "Changes existing customer address"
expecting
payload changeCustomerParameter
delivering
payload D<bool>
// ** BEGIN PROTECTED REGION for API providers
// ** END PROTECTED REGION for API providers
// Generated from DDD upstream Bounded Context 'CustomerManagementContext' implementing OPEN_HOST_SERVICE (OHS) and PUBLISHED_LANGUAGE (PL).
API provider CustomerManagementContextProvider
// The customer management context is responsible for managing all the data of the insurance companies customers.
offers CustomersAggregate
at endpoint location "http://localhost:8001"
via protocol "RESTfulHTTP"
// ** BEGIN PROTECTED REGION for API clients
// ** END PROTECTED REGION for API clients
// Generated from DDD downstream Bounded Context 'PolicyManagementContext' implementing CONFORMIST (CF).
API client PolicyManagementContextClient
// This bounded context manages the contracts and policies of the customers.
consumes CustomersAggregate
API client CustomerSelfServiceContextClient
// This context represents a web application which allows the customer to login and change basic data records like the address.
consumes CustomersAggregate
IPA
The protected regions allow you to guard data types, endpoints, API providers, and API clients into so that they are not overwritten at re-generation. Thus, you can call the MDSL generator on the same file again and all objects within a protected region will still be there and remain unchanged.
For example, you can move a set of data types into the corresponding protected region if you changed the data types manually after generation and want to protect them:
// Generated from DDD Context Map 'Insurance-Example_Context-Map.cml' at 21.10.2019 17:48:52 CEST.
API description CustomerManagementContextAPI
usage context PUBLIC_API for BACKEND_INTEGRATION
// ** BEGIN PROTECTED REGION for data types
data type Address { "street":D<string>, "postalCode":D<int>, "city":D<string>, "manuallyChangedThisDataType":D<string> }
// ** END PROTECTED REGION for data types
data type AddressId P
data type changeCustomerParameter { "firstname":D<string>, "lastname":D<string> }
// removed the rest here to save space ...
IPA
MDSL Support
The current version of our MDSL generator is compatible with the MDSL Version 5. For further questions regarding MDSL, please visit its website https://microservice-api-patterns.github.io/MDSL-Specification/. This is the update site to install the MDSL tools plugin.
- Previous
- Next