# A Model-Driven Method for Fast Building Consistent Web Services from OpenAPI-Compatible Models

David Sferruzza, Jérôme Rocheteau, Christian Attiogbé, Arnaud Lanoix

► To cite this version:

David Sferruzza, Jérôme Rocheteau, Christian Attiogbé, Arnaud Lanoix. A Model-Driven Method for Fast Building Consistent Web Services from OpenAPI-Compatible Models. Model-Driven Engineering and Software Development, 2019, 6th International Conference, MODELSWARD 2018, Funchal, Madeira, Portugal, January 22-24, 2018, Revised Selected Papers. hal-02075980

HAL Id: hal-02075980

https://hal.science/hal-02075980

Submitted on 21 Mar 2019

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>
# A Model-Driven Method for Fast Building Consistent Web Services from OpenAPI-Compatible Models

David Sferruzza<fnref n="1" />,<fnref n="3" />, Jérôme Rocheteau<fnref n="1" />,<fnref n="2" />, Christian Attiogbé<fnref n="1" />, and Arnaud Lanoix<fnref n="1" />

**Abstract.** Lots of software companies rely on web technologies to test market hypotheses in order to develop viable businesses. They often need to quickly build web services that are at the core of their Minimum Viable Products (MVPs). MVPs must be reliable whereas they are based on specifications and hypotheses that are likely to change. Web services need to be well documented, to make it easy to develop applications that consume them. Model Driven Engineering approaches have been proposed and used to develop and evolve web services on one hand, and document them on the other hand. However, these approaches lack the ability to be suitable for both (i) rapid prototyping, (ii) model verification, (iii) compatibility with common programming languages and (iv) alignment between documentation and implementation. Here we propose a meta-model to express web services, the related tool to verify models consistency and an integration of this approach into the OpenAPI Specification. We adopt a shallow verification process to allow rapid prototyping by developers who are not formal methods experts, while still offering design-time guarantees that improve product quality and development efficiency. Web services are defined using parametric components which enable to express and formally verify web service patterns and to safely reuse them in other contexts. We built a tool to check consistency of extended OpenAPI 3.0 models and associated components implementations in order to generate corresponding web services. This allows us to give flexibility and verification support to developers, even in the context of an incremental development, as illustrated by a case study.

**Keywords:** Web Applications, Web Services, Model-Driven Engineering, Formal Verification, Code Generation, OpenAPI 3.0

<footnote n="1">¹ LS2N - UMR CNRS 6004 / F-44322 Nantes Cedex 3, France
[david.sferruzza@ls2n.fr](mailto:david.sferruzza@ls2n.fr),
[christian.attiogbe@ls2n.fr](mailto:christian.attiogbe@ls2n.fr),
[arnaud.lanoix@ls2n.fr](mailto:arnaud.lanoix@ls2n.fr)</footnote>

<footnote n="2">² ICAM / 35, avenue du Champ de Manœuvres, 44470 Carquefou, France
[jerome.rocheteau@icam.fr](mailto:jerome.rocheteau@icam.fr)</footnote>

<footnote n="3">³ Startup Palace / 18, rue Scribe, 44000 Nantes, France
[david.sferruzza@startup-palace.com](mailto:david.sferruzza@startup-palace.com)</footnote>
# 1 Introduction

*Context.* Web agencies are software companies that often work with their customers to help them develop new projects involving the web. Most of these customers need web applications to bring value to their own customers. These web applications are built iteratively to limit their cost while allowing startups to converge toward a viable market. This approach begins by building a Minimum Viable Product (MVP). In this context, it is a web application with a high level of quality but a limited set of features. MVPs (among other kinds of web applications) need to be functional, reliable, usable and designed with users' emotions in mind, with only the features required to test market hypotheses. In this perspective, it is common to develop standalone web services that will be consumed by one or several applications which may provide user interfaces (UIs). For example: one instance of web services can be simultaneously used by several UIs, such as several mobile applications (Android, iOS, ...) and a web application. This allows separation of concerns and centralization of data and process logic, even in the case where there is only one consumer application. Therefore, for *Startup Palace*^4, a web agency that evolves in this context, the process of designing, building and evolving web services is important, because it is at the center of the MVP approach. When developing a MVP it is important to focus on features that really bring value to users, also called *game-changers*. Other features, called *show-stoppers*, do not bring value directly but are required to make the application functional, reliable or usable. *Show-stopper* features are often tedious to implement and so error-prone. Furthermore, because of the iterative process and the purpose of MVPs, specifications are likely to evolve, which can introduce bugs.

Moreover, it is important to design web services in a way that makes them actually usable. This implies providing a good documentation that can be used by developers writing consumer applications or by these consumer applications if they can adapt their behavior dynamically. While this kind of documentation can take many forms, some standards such as OpenAPI [11] and RAML [15] are widely used by the industry. These standards define specifications which describe HTTP APIs of web services and are the center of ecosystems of tools. This article focuses on OpenAPI 3.0 because it is the standard used at *Startup Palace*.

*Motivation.* Developers need abstractions to be able to safely express, isolate, reuse and evolve features. Some programming languages do provide such abstractions, along with modern type checkers that are able to statically verify consistency of programs. But this is not practicable in our context because we would like to leverage existing expertises of developers instead of forcing them to learn a new programming language and its ecosystem from scratch. We need to master the development of *show-stoppers*. We want to fix a common issue related to the tools around OpenAPI which rises when the process of evolving

<footnote>^4 https://www.startup-palace.com</footnote>
the web services occurs. These tools can be used in two main ways. First, with a forward engineering process, developers create manually an OpenAPI model, and use a tool to transform it. For example: a one-time generation of an implementation skeleton in a given technology. Second, with a reverse engineering process, a tool is used to extract an OpenAPI model from a working implementation (which can be enhanced by annotations). While the second process is useful to document existing web services, it cannot leverage the benefits of building web service using a top-down approach: from a high-level (model) to a low-level (implementation). Yet [6] shows the advantages of using different languages for programming-in-the-large and for programming-in-the-small. The first process makes possible to partially leverage these advantages, but the issue is that it lacks the ability to keep the OpenAPI model and the implementation aligned throughout the life of the project. Indeed, implementation is often obtained by a projection of the model that is then manually modified to implement business logic. Any following model evolution needs to be projected again which would require manual modifications to be re-applied from the beginning.

*Contribution.* This work is an attempt to solve the problem of building web services using safe abstractions on top of an existing programming language in order to ease development and reuse of *show-stopper* features, while keeping the web services in sync with their documentation (i.e. an OpenAPI model).

We introduce a meta-model to express web services and a corresponding semantics for verification. This leverages existing theory in Model Driven Engineering (MDE) [3, 16, 17] and a component-based approach in order to provide an expressive and language-agnostic solution to build web services. This meta-model does not allow to completely specify web services but is rather limited to a high-level representation in order to provide support while keeping models simple.

In [19] we have proposed a tool (i) to check models consistency and (ii) to generate working web services from a given valid model. As example, every component preconditions must be fulfilled in their instance contexts in order for a model to be consistent. This mechanism, coupled with the consistency checking, gives developers means to quickly and safely write and use components, and to reuse them in a reliable way.

We integrate the proposed approach in a top-down design process that relies on extended OpenAPI models. Extensions contain the parts of our meta-model that do not have any equivalent in the OpenAPI specification: the business logic. Because the OpenAPI 3.0 meta-model and ours are merged, the resulting models provide single sources of truth for building both web services and their documentation.

This article is an extended version of [21], published in the proceedings of MODELSWARD 2018. It differs by the following aspects:

1. the meta-model was slightly improved in order to add the possibility to better specify service parameters, to improve the type system and to make
re-usability easier by adding a mechanism to bind parameters in component
definitions and in their arguments in instances' contexts;

2. the whole approach was redesigned as part of a more practical top-down workflow based on tools such as OpenAPI instead of being completely stand-alone;

3. this workflow is tested in a two-steps case study, showing how it works in the context of incremental development.

The article is structured as follows. Section 2 presents related work. Section 3 describes the meta-model of web services. Section 4 defines consistency of compliant models and shows how they can be checked. Section 5 shows how this approach can be integrated with OpenAPI. Section 6 introduces a tool to generate actual implementations from models. Section 7 shows a two-steps case study that illustrates our approach. Finally, Section 8 concludes the article with some lessons and future work.

## 2 Related Work

The use of MDE for development and automatic generation of web services or
web applications is not a new topic [3, 2, 17]. Indeed, this work is built on top
of the approach of SWSG [21] and REIFIER [16].

SWSG shares the meta-modeling approach with tools such as *M3D* (introduced in [3] and extended in [2]) that also focus on building web services using MDE. One of the main differences between SWSG and *M3D* is that SWSG was developed with a focus on design-time support. For example, it allows to automatically verify some properties about the structural consistency of models. Even if SWSG is definitely related to existing standards such as BPEL [7] or WSDL, our approach differs on several aspects. First, we want to avoid the shortcomings described in [8], that is WSDL models contain too much technical details and are difficult to understand for humans. Indeed our meta-model is simpler and less expressive than WSDL or BPEL. Second, this allows SWSG to provide more support to users; the balance between flexibility and support is discussed in [27]. Finally, SWSG now relies on OpenAPI.

OpenAPI [11] that is also involved in various research areas. In [4], it was
chosen for its popularity over WADL and other industry standards in order
to automatically transform plain HTML documentations of web services to a
machine-readable format. Moreover, [5] provides a great state of the art of ser-
vice description formats that brings out OpenAPI as the most promising choice
at the moment and enriches it with semantic annotations. It seems to be an
updated version of the work proposed in [26, §3.2]. [18] shows an approach that
is agnostic to service description formats but uses OpenAPI in the article. The
popularity of OpenAPI is also highlighted by its use in other domains. For ex-
ample [30] describes a case where OpenAPI 2.0 is used in combination of other
tools from the life sciences community and points out that the specification ex-
tension mechanism of OpenAPI 3.0 (that we use in this article) might be an
interesting opportunity of improvement. Another example in the telecommuni-
cation domain is presented in [14] which provides a section to emphasizes the
trade-offs of Model-Driven Engineering and argues that they can be overcomed
“with increased investment in the tools that support the development process”;
we share the same vision in the present proposal.

One of the tools featured in the OpenAPI ecosystem is *Swagger Code Generator* [22]. It aims at generating client libraries, server stubs or documentations from an OpenAPI model. The server stubs generation supports many languages and frameworks, but, as its name states, only generates stubs. This helps developers to write new services by generated boilerplate code, which is a tedious task, but they still need to add a lot of code on top of it. Moreover, when services evolve, developer need to manually propagate evolutions into the code-base because *Swagger Code Generator* isn't able to merge them automatically. Our approach solves this issue because it gives flexibility to developers before the code generation step, making useless editing generated code. It is worth mentioning that the code generators in themselves are based on similar model-to-text mechanisms: *Swagger Code Generator* uses the mustache<fnref n="5" /> or the Handlebars<fnref n="6" /> (depending on the version) template format whereas SWSG uses Twirl<fnref n="7" />. Finally, SWSG supports OpenAPI 3.0 models which is not the case of *Swagger Code Generator* at the time of writing<fnref n="8" />.

3 A Meta-Model to Express Web Services

We introduce a meta-model of web services. This meta-model is voluntarily sim-
ple in order to provide two advantages: (i) to give developers good abstractions
to write reusable code while giving them a good flexibility and (ii) to allow tools
to provide support to developers, such as design-time consistency verification
(see Section 4). This is obtained with a trade-off on a lower verification aspects.

3.1 Preliminary Notations

Union. The union or sum type of two types $T_1$ and $T_2$ is denoted $T_1 \uplus T_2$.

Tuple. A tuple $T$ is a product type between $n$ types $T_1$ to $T_n$, with $n \ge 2$. It is denoted $T = T_1 \times \dots \times T_n$. A value $t$ of type $T$ is written as $t = (t_1, \dots, t_n)$ where $t_1 \in T_1, \dots, t_n \in T_n$. The notation $t(x)$ is also used to designate $t_x$, where $x \in \{1, \dots, n\}$.

<footnote n="5">⁵ https://mustache.github.io/</footnote>

<footnote n="6">⁶ https://handlebarsjs.com/</footnote>

<footnote n="7">⁷ https://github.com/playframework/twirl</footnote>

<footnote n="8">⁸ The unstable version 3.0.0-rc0 does support OpenAPI 3.0 but is not yet finished and handle only a few languages and frameworks.</footnote>
*Record.* A record *R* is a tuple with labeled elements. It is denoted $R \equiv \langle label_1 : T_1, \dots, label_n : T_n \rangle$. It is syntactic sugar over a tuple $T_1 \times \dots \times T_n$ and *n* functions *label*₁ : *R* → *T*₁, ..., *label*ₙ : *R* → *T*ₙ. As for tuples, a value *r* of type *R* is written as *r* = (*t*₁, ..., *t*ₙ) where *t*₁ ∈ *T*₁, ..., *t*ₙ ∈ *T*ₙ. Associated functions can also be written *r.label*₁, ..., *r.label*ₙ. For example, for a record *person* = ("Batman", 35) ∈ ⟨name : String, age : Int⟩ we have *name(person)* = *person.name* = "Batman" ∈ *String*.

*Set.* A set whose elements are all of type *T* has the type $\mathcal{P}(T)$.

*List.* A sequence or list of type *T* is a set of elements of type *T* which are ordered. It is denoted *List*(*T*). It is similar to a function from indexes *I* to values of *T*, that is *List*(*T*) : *I* → *T* where *I* is a 1..*N* and *N* = *card*(*List*(*T*)). It can also be written as *List*(*T*) ≡ [*t*₁, ..., *t*ₙ] where *t*₁, ..., *t*ₙ ∈ *T*.

*Projection.* The projection of a set or list of tuples *S* on the *i*-th element of a tuple is written *Prj*ᵢ(*S*). Similarly, the projection of a set or list of records *S* on one of the elements of a record *label*ᵢ is written *Prj*ᵢ*[label*ᵢ*](*S*). For example, for a set of records *S* ∈ *P*(<em>name : String, age : Int</em>) where *S* = {("Batman", 35), ("Robin", 26)}, *Prj*<sub>*name*</sub>(*S*) has type *P*(String) and is equal to {"Batman", "Robin"}.

## 3.2 A Meta-Model of Web Services

A meta-model of web services is defined by the BNF grammar given in Figure 1. Figure 2 shows a slightly simplified version of this grammar in the form of a UML class diagram, to provide a quick glance.

This meta-model does not aim to replace existing standard meta-models like for instance the OpenAPI Specification [11] or RAML [15], but rather to be compatible and complementary with them (see Section 5). These meta-models allow to define programming language-agnostic interface descriptions for HTTP APIs (i.e. informal contracts) in order to be able for both humans and computers to discover and understand their capabilities, while our meta-model allows to express actual implementations of such HTTP APIs. Precisely, our meta-model is designed in order to match needs and requirements about verification (see Section 4), generation and ease of writing (see Section 6). For these reasons, and to be more accessible to practitioners, our approach does not rely on existing standards such as BPEL [7] or WSDL [8].

*Model.* A model of web services $m_i \in M$ is specified as a record of three elements: a set of entities of type *E* that stands as data model, a set of components of type *C* that stands as process model and an ordered list of services of type *S* that exposes component to the outer world: $M \equiv \langle \text{entities} : \mathcal{P}(E), \text{components} : \mathcal{P}(C), \text{services} : \text{List}(S) \rangle$.
<table><caption>**Fig. 1.** Meta-model BNF grammar</caption><tr><td>model</td><td>::= &lt;entities: entity*, components: component*, services: service*&gt;</td></tr><tr><td>identifier</td><td>::= [A-Za-z] [A-Za-z0-9_]*</td></tr><tr><td>entity</td><td>::= &lt;name: identifier, attributes: variable*&gt;</td></tr><tr><td>term</td><td>::= variable | constant</td></tr><tr><td>variable</td><td>::= &lt;name: string, type: type&gt;</td></tr><tr><td>constant</td><td>::= &lt;type: type, value: object&gt;</td></tr><tr><td>type</td><td>::= string | boolean | integer | float | date | datetime | entity-ref | seq-of | option-of</td></tr><tr><td>entity-ref</td><td>::= &lt;entity: identifier&gt;</td></tr><tr><td>seq-of</td><td>::= &lt;seqOf: type&gt;</td></tr><tr><td>option-of</td><td>::= &lt;optionOf: type&gt;</td></tr><tr><td>component</td><td>::= atomic-component | composite-component</td></tr><tr><td>atomic-component</td><td>::= &lt;name: identifier, params: variable*, pre: variable*, add: variable*, rem: variable*&gt;</td></tr><tr><td>composite-component</td><td>::= &lt;name: identifier, params: variable*, components: component-instance*&gt;</td></tr><tr><td>component-instance</td><td>::= &lt;component: identifier, bindings: binding*, aliases: alias*&gt;</td></tr><tr><td>binding</td><td>::= &lt;param: variable, argument: term&gt;</td></tr><tr><td>alias</td><td>::= &lt;source: variable, target: variable&gt;</td></tr><tr><td>service</td><td>::= &lt;method: method, path: path, params: service-parameter*, component: component-instance&gt;</td></tr><tr><td>method</td><td>::= [A-Z]+</td></tr><tr><td>path</td><td>::= .+</td></tr><tr><td>service-parameter</td><td>::= &lt;location: parameter-location, variable: variable&gt;</td></tr><tr><td>parameter-location</td><td>::= query | header | path | cookie | body</td></tr></table>

**Entity and type.** Entities are non-primitive data types. An entity $e_i \in E$ is represented by a record of two elements: a name and a set of variables that represent attributes: $E \equiv \langle name : Id, attributes : P(V) \rangle$. An attribute of an entity can be another entity, making it a recursive type. An identifier $id_i \in Id$ is a string that matches the following regular expression: `^[A-Za-z] [A-Za-z0-9_]*$`. A variable $v_i \in V$ is defined by a record composed of a name and a type: $V \equiv \langle name : String, type : T \rangle$. A type $t_i \in T$ can be primitive or parametric. A primitive type can be one of the followings: `String`, `Boolean`, `Integer`, `Float`, `Date` or `DateTime`. Parametric types are represented by records that have one element by parameter of the type; available parametric types are defined in Table 1.

**Components.** Components are units of processes and computations that occur inside web services. Their execution happens in an isolated context, that can contain variables. They can mutate this context by adding and removing variables. A component $c_i \in C$ is defined as the union type of atomic components `AC` and composite components `CC`: $C \triangleq AC \cup CC$. Both types of components are defined by a name and a set of variables that express components' parameters. Because they have parameters, we call them parametric components. An atomic compo-
<figure><img src="image_2.png" /><figcaption>Fig. 2. Meta-model diagram</figcaption></figure>

A component `ac_i` is represented by a record of the following elements: name, parameters, preconditions (a set of variables that might be needed in the execution context), additions (a set of variables that will be added to the execution context) and removals (a set of variables that will be removed from the execution context):

`AC = ⟨name : Id, params : P(V), pre : P(V), add : P(V), rem : P(V)⟩`. The last three elements are sometimes referred to as the component's contract. A composite component `cc_i` is represented by a record of the following elements: name, parameters and an ordered list of component instances:

`CC = ⟨name : Id, params : P(V), components : List(CI)⟩`. A component instance `ci` is represented by a record of three elements: a component, a set of bindings used to instantiate the component by associating arguments to

<table><caption>Table 1. Parametric types</caption><thead><tr><th>Type constructor</th><th>Parameters</th><th>Description</th></tr></thead><tbody><tr><td>Entity</td><td>⟨entity : Id⟩</td><td>A reference to an entity. The element <strong>entity</strong> given in parameter must correspond to the name of an entity defined in the model.</td></tr><tr><td>SeqOf</td><td>⟨seqOf : T⟩</td><td>A sequence of elements of a given type.</td></tr><tr><td>OptionOf</td><td>⟨optionOf : T⟩</td><td>The optional version of a given type, that is every element of the given type plus a special value <strong>null</strong> that represent the absence of value.</td></tr></tbody></table>
its parameters and a set of aliases that allow to rename variables of the compo-
nent’s contract on the instantiation context: $CI \equiv \langle component : C, bindings : P(\langle param : V, argument : Term \rangle), aliases : P(\langle source : Id, target : Id \rangle) \rangle$.
Terms can be variables or constant literal values (Const): $Term \triangleq V \cup Const$.
Atomic components are meant to be along with an implementation written using
a programming language whereas composite components are not. Components
can be seen as an abstraction to encourage separation of concerns and reusability
by leveraging two mechanisms: composition and parametrization.

*Service*. Services are the entry points of web services. A service $s_i \in S$ is represented by a record of four elements: a HTTP method, a path, a set of expected input parameters and a component instance: $S \equiv \langle method : M, path : P, params : P(\langle location : L, variable : V \rangle), component : CI \rangle$. A method $m_i \in M$ is a valid HTTP method name, as defined by RFC 7231⁹. A path $p_i \in P$ is a relative URL that can contain parameters whose names are to be placed inside braces; for example: `/user/{id}`. A location $l_i \in L$ represents where a given service parameter can be found in a HTTP request: $L \equiv \{query, header, path, cookie, body\}$. In a model of web services, services are gathered in an ordered list. This abstraction is very common in web frameworks and is often called *router*. Instead of considering a web application as a huge function of HTTP requests to HTTP responses, a router allows to dispatch HTTP requests to several such functions by filtering them declaratively by method and by path. That is to reduce complexity of the whole application by encouraging separation of concerns.

## 3.3 Concrete Syntax for Models of Web Services

We previously presented a mathematical definition of our meta-model in Sec-
tion 3.2 and an equivalent BNF grammar in Figure 1. These notations are meant
to introduce formal definitions that are used to define properties on the meta-
model, which we do in Section 4. But they are cumbersome to read or write
actual models.

We also introduce a concrete syntax that is more compact and readable.
Because it is equivalent to BNF grammar in Figure 1, we won’t define it formally
here but only provide some intuition on it.

A model is represented, with respect to its formal definition, as an unordered
list of definitions, each on its own lines. A definition starts with an element iden-
tifier: *e* for entity, *s* for service, *ac* for atomic component and *cc* for composite
component. Element’s properties are placed on their own indented line and pre-
fixed with the property name (or a short alias). Listing 1 gives an example of a
service declaration.

For readability reasons, every item of a service parameters set must be written
on its own line. For example line 4 of Listing 1 shows a **param** item instead of
the whole **params** set.

<footnote n="9">⁹ https://tools.ietf.org/html/rfc7231</footnote>
<caption>Listing 1. Definition of a service using the concrete syntax</caption>

```
s
method POST
path / name/{email}
param path email: String

Get Name
```

The concrete syntax of the other structures of the meta-model is defined in a similar way. A full example is available in the repository of SWSG<fnref n="10" />.

## 3.4 Evaluating a Model of Web Services

Our meta-model gives helpful abstractions to develop web services but the built models need a rigorous evaluation semantics. The following successive steps describe how web services based on an instance of this meta-model could handle incoming HTTP requests.

*Routing.* First, the application receives a HTTP request. Its list of services is sequentially scanned until a service matches the request; that is, the HTTP method is the same and the URL matches the path. If no service matches, then a static 404 HTTP response is sent back.

*Flattening.* The component instance contained in the service is reduced to a flattened ordered list of instances of atomic components. Arguments passed to the different components (through bindings in component instances) are resolved so that they become only constants (no more variables), and aliases are propagated to instantiated components and their subcomponents. Instances of composite components are then recursively replaced by their subcomponents. We define by cases a function *flatten(m, c)* (with $m \in M$) that flattens a given component:

$$ flatten(m, c) = \begin{cases} [c] & \text{if } c \in AC \\ \bigcup_{ci \in c components} flatten(m, ci) & \text{if } c \in CC \\ flatten(m, c.component) & \text{if } c \in CI \\ flatten(m, resolve_c(m, c)) & \text{if } c \in Id \end{cases} \quad (1) $$

The function *resolve*<sub>c</sub> used in Formula (1) takes two parameters: a model and a component name. It outputs the definition of the component with the given name in the given model. It is of type $M \times Id \rightarrow C$. When called on a model that verifies the consistency rules defined in Section 4 and on a component name extracted from such a model, this function returns a deterministic result.

<footnote n="10">¹⁰ https://gitlab.startup-palace.com/research/swsg/blob/master/examples/registration/registration.model</footnote>
*Evaluating components.* An initial evaluation context is created by extracting parameters (if present) from the request URL and putting them into an empty context. This flattened list is then evaluated: every atomic component is executed given the previous context as an input and produces a new context as an output. This behavior is very similar to state monads (see [29, § 2.5]).

*Responding.* Finally, a HTTP response is built. There are two cases to consider. If one of the evaluated components returned a HTTP response instead of a new context, the following components are not evaluated and this response is returned to the client. Otherwise, the context is serialized and encapsulated into a HTTP response of code 200.

# 4 Consistency of Web Services

Section 3 showed that our web services meta-model uses components as an abstraction to improve separation of concerns and reusability. In order to allow developers to safely use this abstraction we propose a way to do verification of models. This verification checks if a model is consistent. It can happen at *design-time* – outside any evaluation context – so that inconsistent models won’t be run in production.

*Definition.* A model of web services $m \in M$ is consistent if it verifies all the following properties identified by Formulas (2) to (22).

*Component name unicity.* Every component in a model has a unique name.

$$ \forall c, c' \in m/components \cdot (c.name = c'.name \Rightarrow c = c') \quad (2) $$

*Entity name unicity.* Every entity in a model has a unique name.

$$ \forall e, e' \in m/entities \cdot (e.name = e'.name \Rightarrow e = e') \quad (3) $$

*Attribute name unicity.* Every attribute of an entity has a unique name.

$$ \forall e \in m/entities, \forall a, a' \in e_attributes \cdot (a.name = a'.name \Rightarrow a = a') \quad (4) $$

*Service parameter name unicity.* Every parameter of a service has a unique name.

$$ \forall s \in m/services, \forall p, p' \in s.params \cdot (p.name = p'.name \Rightarrow p = p') \quad (5) $$

*Parameters of location *body* unicity.* There is a maximum of one parameter per service that has its location equal to *body*.

$$ \forall s \in m/services, \forall p, p' \in s.params \cdot (p.location = p'.location = body \Rightarrow p = p') \quad (6) $$
*Reference consistency.* Table 2 describes the reference locations in a model. It gives formulas to extract from a model every possible set of references to entities or components.

<table><caption>**Table 2.** Exhaustive list of reference locations</caption><thead><tr><th>Referenced element</th><th>References locations</th></tr></thead><tbody><tr><td>Component C</td><td>refs<sub>c1</sub> = Prj<sub>components.component</sub>(m.<sub>components</sub> ∩ CC)<br>refs<sub>c2</sub> = Prj<sub>component.component</sub>(m.<sub>services</sub>)</td></tr><tr><td>Entity E</td><td>refs<sub>e1</sub> = Prj<sub>entity</sub>(Prj<sub>attributes.type</sub>(m.<sub>entities</sub>) ∩ EntityRef)<br>refs<sub>e2</sub> = Prj<sub>entity</sub>(Prj<sub>params.type</sub>(m.<sub>components</sub>) ∩ EntityRef)<br>refs<sub>e3</sub> = Prj<sub>entity</sub>(Prj<sub>pre.type</sub>(m.<sub>components</sub> ∩ AC) ∩ EntityRef)<br>refs<sub>e4</sub> = Prj<sub>entity</sub>(Prj<sub>add.type</sub>(m.<sub>components</sub> ∩ AC) ∩ EntityRef)<br>refs<sub>e5</sub> = Prj<sub>entity</sub>(Prj<sub>rem.type</sub>(m.<sub>components</sub> ∩ AC) ∩ EntityRef)<br>refs<sub>e6</sub> = Prj<sub>entity</sub>(Prj<sub>params variable.type</sub>(m.<sub>services</sub>) ∩ EntityRef)</td></tr></tbody></table>

Every reference to a component designates an element that exists in the model.

$$ \forall ref \in (refs_{c1} \cup refs_{c2}), \exists c \in m.<sub>components</sub>.(c.<sub>name</sub> = ref) \quad (7) $$

Every reference to an entity designates an element that exists in the model.

$$ \begin{array}{l} \forall ref \in (refs_{e1} \cup refs_{e2} \cup refs_{e3} \cup refs_{e4} \cup refs_{e5} \cup refs_{e6}), \\ \quad \exists e \in m.<sub>entities</sub>.(e.<sub>name</sub> = ref) \quad (8) \end{array} $$

*Component context variable name unicity.* Variables in atomic components can- not have the same name if they are not identical.

$$ \begin{array}{l} \forall c \in (m.<sub>components</sub> \cap AC), \forall v, v' \in (c.<sub>pre</sub> \cup c.<sub>add</sub> \cup c.<sub>del</sub>). \\ (v.<sub>name</sub> = v'.<sub>name</sub> \Rightarrow v = v') \quad (9) \end{array} $$

*Composite component non emptiness.* Composite components must have sub-components.

$$ \forall c \in (m.<sub>components</sub> \cap CC).(c.<sub>components</sub> \neq \emptyset) \quad (10) $$

*Alias source unicity.* The source name of an alias is unique in its component instance.

$$ \begin{array}{l} \forall ci \in (Prj<sub>components</sub>(m.<sub>components</sub> \cap CC) \cup Prj<sub>component</sub>(m.<sub>services</sub>)), \\ \quad \forall a, a' \in ci.<sub>alias</sub>.(a.<sub>source</sub> = a'.<sub>source</sub> \Rightarrow a = a') \quad (11) \end{array} $$
*Alias target unicity.* The target name of an alias is unique in its component instance.

$$ \forall ci \in (\text{Prj}_{\text{components}}(m.\text{components} \cap \text{CC}) \cup \text{Prj}_{\text{component}}(m.\text{services})), \\ \forall a, a' \in ci.\text{alias}.(a.\text{target} = a'.\text{target} \Rightarrow a = a') \quad (12) $$

*Recursive reference consistency.* An entity is not referenced by its transitive attributes.

$$ \forall e \in m.\text{entities}.\text{(EntityRef(e.name) \notin \text{deps}_e(m, EntityRef(e.name)))} \quad (13) $$

where the function `deps_e`: $M \times T \to \mathcal{P}(T)$ returns the set of transitive dependencies of a given type. `deps_e(m, t)` is defined as follows:

$$ \text{deps}_e(m, t) = \begin{cases} \text{deps}_e(m, t') & \text{if } t = \text{OptionOf}(t') \\ \text{deps}_e(m, t') & \text{if } t = \text{SeqOf}(t') \\ t'' \cup \bigcup_{t_i \in t''} \text{deps}_e(m, t_i) & \text{if } t = \text{EntityRef}(name) \\ \text{deps}_e(m, t) = \emptyset & \text{otherwise} \end{cases} $$ 
with $t'' = \text{Prj}_{\text{type}}(\text{resolve}_e(m, name).\text{attributes})$

where the function `resolve_e`: $M \times \text{Id} \to E$ returns the definition of the entity that as the same name as the one given in the second parameter.

The same is true for composite components that are not referenced from their transitive subcomponents.

$$ \forall c \in (m.\text{components} \cap \text{CC}).(c.name \notin \text{deps}_c(m, c)) \quad (14) $$

where the function `deps_c`: $M \times C \to \mathcal{P}(\text{Id})$ returns the set of transitive dependencies of a given component. `deps_c(m, c)` is defined as follows:

$$ \text{deps}_c(m, c) = \begin{cases} \emptyset & \text{if } c \in AC \\ \text{Prj}_{\text{name}}(c.\text{components}) \cup \bigcup_{c_i \in c.\text{components}} \text{deps}_c(m, \text{resolve}_c(m, c_i)) & \text{if } c \in CC \end{cases} $$

where the function `resolve_c`: $M \times \text{Id} \to C$ returns the definition of the component that has the same name as the one given in the second parameter, as defined in Section 3.4.

*Alias source validity.* The source name of an alias corresponds to the name of a variable of the contract of the instantiated component.

$$ \forall ci \in (\text{Prj}_{\text{components}}(m.\text{components} \cap \text{CC}) \cup \text{Prj}_{\text{component}}(m.\text{services})), \\ \forall a \in ci.\text{alias}.(c = \text{resolve}_c(m, ci.\text{component}) \Rightarrow \\ a.\text{source} \in \text{Prj}_{\text{name}}(c.\text{pre} \cup c.\text{add} \cup c.\text{rem})) \quad (15) $$
*Alias target validity.* The target name of an alias corresponds to the name of a variable that will be added to the execution context by the instantiated component.

$$ 
\begin{array}{l}
\forall ci \in (Prj_{components}(m/components \cap CC) \cup Prj_{component}(m/services)), \\
\qquad \forall a \in ci.\text{alias}. \\
(c = resolve_c(m, ci.component) \Rightarrow a.target \notin Prj_{name}(c.add)) \quad (16)
\end{array}
$$

*Service path validity.* The set of names of service parameters that have a **path** location is exactly the same as the set of parameter names declared in the path string of a service.

$$ \forall s \in m.services. (\{p \in s.params | p.location = Path\} = extract(s.path)) \quad (17) $$

where the function *extract*: $P \to \mathcal{P}(V)$ extracts parameter names from a path; that is, for a given path, it returns the contents of the first matching parenthesis of the regular expression $\{([A-Za-z0-9_]+)\}$.

*Component context immutability.* Components don't override existing variables of the context. Every atomic component does not add a new variable to its output context if there is already a variable with the same name in its input context.

$$ \forall c \in (m/components \cap AC) \cdot (c.add \cap c.pre = \emptyset) \quad (18) $$

*Component precondition exhaustivity.* Components depend on the variables they remove. Every atomic component has each variable it will remove from the context in its preconditions.

$$ \forall c \in (m/components \cap AC) \cdot (c.rem \subseteq c.pre) \quad (19) $$

*Component instance bindings consistency.* Component instances have bindings that associate a term to a variable. Every binding associates a term to a variable of the same type.

$$ 
\begin{array}{l}
\forall ci \in (Prj_{components}(m/components \cap CC) \cup Prj_{component}(m/services)), \\
\qquad \forall b \in ci.binding. (bvariable.type = bargument.type) \quad (20)
\end{array}
$$

*Component instance parameters exhaustivity.* Component instances provide values for every parameter of the instantiated component. Every component instance provides exactly as much arguments as the component it instantiates needs parameters. Names and types of the arguments match those of the parameters.

$$ 
\begin{array}{l}
\forall ci \in (Prj_{components}(m/components \cap CC) \cup Prj_{component}(m/services)), \\
\qquad \exists c \in m/components. \\
(c.name = ci.component \Rightarrow Prj_{param}(ci.binding) = c.params) \quad (21)
\end{array}
$$
Context validity. Components are instantiated in contexts that fulfill their pre-conditions. When building flat ordered lists of atomic components for each service (see Section 3.4), every atomic component of these lists has its preconditions fulfilled by its input context:

$$ \forall s \in S \cdot (flatten(s.component) \diamond s.params) \qquad (22) $$

where ◦ is a function of List(AC) × P(V) → Boolean in infix notation<fnref n="11" /> that is true when applied to a component and a context that satisfies the component’s preconditions. It is defined by the following semantic rules:

$$ \frac{\begin{array}{c} \mathrm{ctx}_0 \in \mathcal{P}(V) \\ \forall i \in [0, n], c_i \in \mathrm{CI} \end{array}}{[c_0, \dots, c_n] \diamond \mathrm{ctx}_0} \quad \begin{array}{c} \mathrm{ctx}_1 = \mathrm{ctx}_0 \cup c_0.add \setminus c_0.rem \\ [c_1, \dots, c_n] \diamond \mathrm{ctx}_1 \end{array} $$

$$ \frac{\begin{array}{c} \mathrm{pre}, \mathrm{ctx} \in \mathcal{P}(V) \\ \forall v \in \mathrm{pre}.v \in \mathrm{ctx} \lor (\exists v' \in \mathrm{ctx}, v.name = v'.name \\ \qquad \land v'.type = \mathrm{OptionOf}(v.type)) \end{array}}{\mathrm{pre} \subseteq \mathrm{ctx}} $$

pre ⊆< ctx means that the component’s preconditions pre are satisfied by the context ctx. We use ⊆< instead of ⊆ because ⊆< requires the types to be strictly identical, which we do not want in order to handle optional types.

# 5 Development Process Integrated to OpenAPI

In Section 3 we introduced a meta-model of web services, along with rules to check its consistency in Section 4. In Section 5.1 we introduce a common development process involving OpenAPI. This gives some background on OpenAPI and prepares to the merger of our approach in OpenAPI’s in Section 5.2.

## 5.1 A Common Usage of OpenAPI

The OpenAPI Specification [11] defines a standard to express interfaces to HTTP APIs in a language-agnostic way. It aims at allowing “both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection” [11]: that is a meta-model. As in MDE, the point of having meta-models is to have tools that can rely on them in order to safely manipulate models and offer support to developers. Indeed, an ecosystem of tools was developed around OpenAPI by

<footnote n="11">¹¹ `cs` ◦ `ctx` is equivalent to ◦ (`cs`, `ctx`)</footnote>
various actors. Such tools have several purposes, including but not limited to: providing an interactive graphical user interface from a model [23, 24], generating functional tests from a model [1] or generating a model from an annotated implementation [28].

*The Petstore example.* To ease the development of tools, some official examples of OpenAPI models are shipped with the specification. In this article, we focus on the *Petstore* example [12]. It describes a simple application that exposes four services to list, show, add and remove data records representing animals. We assume that we are in the context of a company such as *Startup Palace*; that means the goal is to develop these web services in order for them to be consumed by user interface applications; for example, desktop and mobile applications for the owner of the store.

*Development process.* The common top-down development process follows. First, developers make several iterations on writing an OpenAPI model. This model must match the functional specifications and describe web services that are fully exploitable by consumer applications. For example, the description of one of the Petstore services is shown in Listing 2. It references schemas that are defined in another part of the model, as shown in Listing 3.

<table><caption>**Listing 2.** A service in the Petstore example</caption><tr><td>/pets/{id}</td><td>1</td></tr><tr><td>get:</td><td>2</td></tr><tr><td>3</td><td><strong>description:</strong> Returns a user based on a single ID, if the user does not have access to the pet</td><td>3</td></tr><tr><td>4</td><td><strong>operationId:</strong> find pet by id</td><td>4</td></tr><tr><td>5</td><td><strong>parameters:</strong></td><td>6</td></tr><tr><td>6</td><td>- <strong>name:</strong> id</td><td>7</td></tr><tr><td>7</td><td>in: <strong>path</strong></td><td>8</td></tr><tr><td>8</td><td><strong>description:</strong> ID of pet to fetch</td><td>9</td></tr><tr><td>9</td><td>required: true</td><td>10</td></tr><tr><td>10</td><td><strong>schema:</strong></td><td>11</td></tr><tr><td>11</td><td>type: <strong>integer</strong></td><td>12</td></tr><tr><td>12</td><td>format: <strong>int64</strong></td><td>13</td></tr><tr><td>13</td><td><strong>responses:</strong></td><td>14</td></tr><tr><td>14</td><td>'200':</td><td>15</td></tr><tr><td>15</td><td><strong>description:</strong> pet response</td><td>16</td></tr><tr><td>16</td><td><strong>content:</strong></td><td>17</td></tr><tr><td>17</td><td>application/json:</td><td>18</td></tr><tr><td>18</td><td>schema:</td><td>19</td></tr><tr><td>19</td><td>$ref: '#/components/schemas/ Pet'</td><td>20</td></tr><tr><td>20</td><td>default:</td><td>21</td></tr><tr><td>21</td><td><strong>description:</strong> unexpected error</td><td>22</td></tr><tr><td>22</td><td><strong>content:</strong></td><td>23</td></tr><tr><td>23</td><td>application/json:</td><td>24</td></tr><tr><td>24</td><td>schema:</td><td>25</td></tr><tr><td>25</td><td>$ref: '#/components/schemas/ Error'</td><td>26</td></tr><tr><td>26</td><td></td><td>27</td></tr><tr><td>27</td><td></td><td>28</td></tr><tr><td>28</td><td></td><td>29</td></tr></table>

<table><caption>**Listing 3.** Schemas in the Petstore example</caption><tr><td>components:</td><td></td></tr><tr><td>schemas:</td><td></td></tr><tr><td>Pet:</td><td></td></tr><tr><td>allOf:</td><td></td></tr><tr><td>- $ref: '#/components/schemas/ NewPet'</td><td></td></tr><tr><td>- required:</td><td></td></tr><tr><td>- id</td><td></td></tr><tr><td>properties:</td><td></td></tr><tr><td>id:</td><td></td></tr><tr><td>type: integer</td><td></td></tr><tr><td>format: int64</td><td></td></tr><tr><td>NewPet:</td><td></td></tr><tr><td>required:</td><td></td></tr><tr><td>- name</td><td></td></tr><tr><td>properties:</td><td></td></tr><tr><td>name:</td><td></td></tr><tr><td>type: string</td><td></td></tr><tr><td>tag:</td><td></td></tr><tr><td>type: string</td><td></td></tr><tr><td>Error:</td><td></td></tr><tr><td>required:</td><td></td></tr><tr><td>- code</td><td></td></tr><tr><td>- message</td><td></td></tr><tr><td>properties:</td><td></td></tr><tr><td>code:</td><td></td></tr><tr><td>type: integer</td><td></td></tr><tr><td>format: int32</td><td></td></tr><tr><td>message:</td><td></td></tr><tr><td>type: string</td><td></td></tr></table>
When this OpenAPI model is stable enough, developers can use it as a spec-
ification to start building web services and consumer applications. There are
some kinds of tools that can take the OpenAPI model as input and help to build
compliant web services; for example, by generating a skeleton of application us-
ing a given technological stack [10], or by generating automated tests that can
be used to check if the web services are conformant [1].

But all these tools have a major limitation: they require humans to manually
update the OpenAPI model whenever the web services evolve. This is a very com-
mon situation: specifications must evolve either because business requirements
have changed or new constraints have been discovered while developing. Even
if some tools can mitigate this issue, it is likely that developers will eventually
stop maintaining the OpenAPI model after the web services reach production,
making the two diverge over time. In long-term projects, this means giving up
on every advantage provided by OpenAPI and its MDE approach.

To fix this shortcoming, we present in Section 6 an improved version of this
process that leverages a meta-model of web services we introduced in [21]. How-
ever, in order for this new approach to be feasible, we first need to extend the
OpenAPI 3.0 Specification.

5.2 Extending OpenAPI 3.0

Recall that the OpenAPI Specification describes a meta-model to express an
interface to web services. But, unlike our approach, it does not describe how web
services are implemented. To get the best of both our approach and OpenAPI's,
we propose a way to merge the latter with our meta-model of web services.

To preserve tools compatibility we make use of *Specification Extensions*, as
defined in OpenAPI 3.0 [11]. This mechanism allows to add data to models
without breaking their compliance to the specification or their ability to be used
by tools designed to be compatible with it. The details of the extensions to
OpenAPI 3.0 can be found in [20]. The following paragraphs present the nature
and the main lines of these extensions for each aspect of our meta-model.

Because an OpenAPI model can be seen as a tree (before references are
resolved) that has the OpenAPI object as root, we make use of the notion of
paths. The components path designate the child of the root named components.
Sub-children are separated using a > symbol; the components > requestBodies
designates the requestBodies child of the components child of the root.

*Data model.* An OpenAPI 3.0 model can contain a set of **Schema** objects<fnref n="12" />. A **Schema** object defines a data type for input or output data, which can then be referenced from elsewhere in the model. This mechanism provides more expressiveness than ours and was made for the same purpose. Thus it is a good fit for the *entities* of our meta-model.

<footnote n="12">¹² In the `components > schemas` path.</footnote>
*Processes*. On purpose, there are no equivalent of our component system in OpenAPI 3.0, because it describes processes that are internal to the web services which is out of OpenAPI's scope. Accordingly we add two properties in the *Components* object of OpenAPI. They contain sets of atomic and composite component definitions<fnref n="13" />. Exact schemas of these components follow on from their definition in our meta-model.

*Services*. Describing services is the main feature of OpenAPI. As such they can be specified in a quite expressive way. Yet the service meta-model in OpenAPI is not a superset of ours because of two lacks that require it to be extended. First, each service must be associated to a component instance that describes its behavior<fnref n="14" />. Second, if a service has a *requestBody* property (that describes the type of the data required in the request body), the *requestBody* object must contain a variable name<fnref n="15" />. When generating the web services code, this is used to include the request body contents in a variable of the execution context.

From now on, we consider OpenAPI 3.0 extended with the process and service parts of our meta-model. OpenAPI's data model is not extended as it was already powerful enough for our needs.

# 6 A Tool to Generate Consistent Web Services

To support the approach of automatically building web services from an extended OpenAPI model (see Section 5), we propose a tool named *Safe Web Services Generator* (SWSG) [19] that automates both consistency verification (see Section 4) and code generation. This tool takes two inputs: a model file (both extended OpenAPI models and our own concrete syntax are supported), and a path to a directory which contains implementations of atomic components.

As shown in Figure 3, our tool follows four sequential steps:

1. **Model parsing** Input model is parsed as concrete syntax of the meta-model (see Section 3.3) or as an extended OpenAPI model (see [20]).

2. **Model transformation** If the parsed model was an OpenAPI model, it is transformed to match our meta-model.

3. **Model consistency verification** The model is checked in order to establish its consistency (see Section 4).

4. **Code generation** The model and the implementations of atomic components are used to generate an implementation of the web services they represent.

<footnote n="13">13 Their paths are `components > x-swsg-ac` and `components > x-swsg-cc`.</footnote>

<footnote n="14">14 In the `x-swsg-ci` property of the service.</footnote>

<footnote n="15">15 In a `x-swsg-name` property.</footnote>
<figure><img src="image_3.png" /><figcaption>Fig. 3. SWSG process</figcaption></figure>

*Model transformation.* Transforming extended OpenAPI models to SWSG models is quite straightforward, except for schemas/types. OpenAPI defines some primitive types and relies on a modified version of the *JSON Schema Specification* [9] for complex types. There are two issues; first, the *JSON Schema Specification* is more expressive than SWSG's type system. For example, it allows to define refined types, e.g. to add a minimum length to a string. Second, it supports both literal and referenced definition of attribute types. Moreover, references are quite expressive and can target many places in the OpenAPI main document or even in another one. In comparison, SWSG only supports literal primitive types or references to other *entities*.

While the second issue is more a technical problem, the first would require to alter SWSG's type system in order for it to support expressing every possible OpenAPI type. Yet, for the sake of simplicity of both our meta-model and our prototype we choose to not address them; they are not essential to test and validate our approach. Therefore our prototype might return errors when working with some OpenAPI models that contain these unsupported types or references in schemas.

*Code generation.* The process defined by Figure 3 is generic: it does not rely on a specific language or technology. Yet the language and technologies used to implement atomic components must be identical or compatible with those of the code generation target. Because we experiment in *Startup Palace's* context, our prototype targets the PHP programming language [25] with the *Laravel* web framework [13], which is a common tool stack.

Many similar tools (see Section 2) take the approach to generate and output a standalone web application that includes everything necessary to operate it. We took another approach by generating code that should never be manually edited; the generated code does not override any existing files in a *Laravel's* architecture and can be easily hooked to an existing web application through configuration.
Because of the considered trade-off between provided support and flexibility left to developers, this MDE approach was designed to allow shallow consistency verification and most inconsistencies in the model are caught at compile-time. This does not prevent developers to create flawed or insecure applications, as they have full control on the atomic components implementations. Indeed this flexibility comes at the cost of a bit of support. However, we believe this trade-off is crucial when the developers have to quickly build web services that might grow and stay in production for a while.

# 7 Experimentations and Discussion

*Process.* We derive a new process from the common OpenAPI process explained in Section 5.1. *Step 1:* design a stable OpenAPI model. *Step 2:* design SWSG components using the extensions we introduced in Section 5 to write them inside the OpenAPI model. Every atomic component defined in the model must be provided with an implementation. *Step 3:* use SWSG to check the model and generate working web services if the verification is successful.

*Case study #1.* *Step 1:* we take the Petstore example [12] presented in Section 5.1. The example service defined by Listing 2 is a part of a standard OpenAPI model. *Step 2:* we need a component that will handle the response generation when this service will receive requests. We create a composite component called FindPet and reference it from the service, as shown in lines 26-27 in Listing 4. This composite component has two children that are atomic components. The first takes an ID as input, uses it to query the database and adds the Pet result to the context. The second takes a Pet, serializes it in JSON and put it in an HTTP response. These three components are defined in Listing 5. Implementations are written for every atomic components. Listing 6 shows the implementation of the GetPetById component as an example<fnref n="16" />.

*Step 3:* we run SWSG on these inputs and get a *PreconditionError*. This verification error indicates that a component's precondition is not fulfilled in a given instanciation context. In the current case, we learn that the GetPetById component misses a string named id when instanciated by the FindPet component in the GET /pet/{id} service. Indeed, we voluntarily introduced an error in Listing 5: the GetPetById component is given an integer (by the service) whereas it requires a string. In this particular example, it should require an integer id variable in order to be consistent with the service parameter. Nevertheless, in more complex projects, this component might have been used inside several other composite components and services. Thus, it might not be a good solution to just change the component's definition because it might break other

<footnote n="16">16 The PHP class in Listing 6 depends on the Component interface and on the Ctx and Params classes. They are defined in code output by the code generator and are just implementation details of the SWSG specification in this specific code generator. Different code generators could require different constraints on implementations of atomic components.</footnote>
<figure><img src="image_4.png" /><figcaption>**Listing 5.** Components in the SWSG Petstore example</figcaption></figure>

<figure><img src="image_5.png" /></figure>

workflows. This is the kind of mistakes SWSG can prevent us to make: because they are reported very early at compile-time, instead of runtime which is too late. Developers can study the problem and decide if they have to build a better implementation or if the process model was badly designed.

When running SWSG on a fixed model, the code generation can proceed. The *Laravel* code generator generates four kinds of files: one file per atomic components (identical to those written manually by the developers; see Listing 6), one file per composite components, a router file and several static files (that do not depend on the model; for example the *Component* interface definition).

**Case study #2.** After it was successfully implemented, we want to extend the Petstore example and add a new service to handle `PUT /pets/{id}` requests. These requests set a pet with the given attributes and the given ID, by creating it or by updating the attributes of an already existing pet that has the same given ID. This is very similar to the `POST /pets` service; the difference is that the latter does not receive any ID, thus it only creates pets, whereas the new service can be used to edit them as well.

To implement this, we follow the same process as earlier. *Step 1:* we extend the OpenAPI model by adding a new service that matches our needs. *Step 2:* we need to attach it to SWSG components. Because of the similarity between this service and the `POST /pets` one, we choose to reuse and extend
<figure><img src="image_6.png" /><figcaption>Listing 6. Implementation of the GetPetById atomic component</figcaption></figure>

existing components. The `CreatePet` atomic component is thereby renamed to `CreateOrUpdatePet`, given a boolean parameter `createOnly` and a new precondition on an optional integer `id` variable. When instanciated for the `POST` service, `createOnly` is given the value `true`, whereas it is `false` when the same component is instanciated from the `PUT` service. Then, the implementation of this atomic component is modified so that it adds or updates pets depending on the value of the `createOnly` parameter and the one of the `id` context variable. Listing 7 shows how the new service instantiates a composite component (defined in Listing 8) that in turn calls the `CreateOrUpdatePet` and passes it the right value for its `createOnly` parameter. Step 3: we run SWSG.

<figure><img src="image_7.png" /><figcaption>Listing 8. The AddOrEditPet component</figcaption></figure>

<figure><img src="image_8.png" /></figure>
*Discussion.* Our approach has several advantages over a regular development process, for example writing the whole application using a programming language and a web framework such as Laravel. First, the model-driven approach forces developers to think of their design or design evolutions at a macroscopic scale before writing low-level implementations. Along with the automatic model verification, this provides them an easy way to spot design mistakes early-on in the process, therefore saving some of their time. In a regular process, developers could only rely on the quality tools offered by the language or the framework. Because there is no static verification step in PHP, for example, and because lots of developers do not write any automated tests, they would often be tempted to skip checking the consistency of their design, especially when making small evolutions on it. Second, because the extended OpenAPI model is used to generate both implementation and documentation, both will stay aligned during the life of the project. In a regular process, this property relies on the will of the developers; they might stop maintaining the OpenAPI model and still make evolutions to the implementation.

As illustrated by the second step of our case study, these advantages are especially valuable for incremental development. Indeed they enforce several forms of consistency at different levels of the projects, for a low cost in term of flexibility and productivity. In contexts such as ours where we need to build and evolve web services for MVPs, incremental development is crucial. *The model and code of this case study are available in the repository of SWSG*<fnref n="17" />.

# 8 Conclusion

We proposed a method integrated to OpenAPI 3.0 to build web services. It is fast, simple, robust and flexible. It is based on a meta-model that allows developers to define implementations of web services, starting from the corresponding high-level contract as expressed by a standard OpenAPI model. Consistency of models can be verified using an operational semantics so that code generated from these models is safe. We built a tool, SWSG, that leverages this process in the technological context of a web company, *Startup Palace*. The whole approach was illustrated on a two-steps case study to show its advantages. Even if one of the motivations was to develop MVPs applications, the approach is not limited to this scope and is suitable to most applications based on web services.

We have several main prospects. First, the type system used to describe component parameters, preconditions and model's entities (among others) is at the core of the consistency verification, yet it is not flexible enough. Making it more expressive, by allowing subtyping in component preconditions for example, while keeping at least the same level of verification might be necessary to reach a good reusability on bigger projects. Another perspective is to allow and ease safe model composition, so that developers can reuse concepts between projects when it makes sense. Model composition is theoretically handled by OpenAPI

<footnote n="17">¹⁷ https://gitlab.startup-palace.com/research/swsg/tree/master/examples/petstore</footnote>
but not currently supported by SWSG. Then, the developer experience could be
improved if there were tools able to automatically check the compliance of atomic
components to their contract in the model. Finally, the whole approach needs
to be evaluated on more realistic and larger case studies. This evaluation must
rely on metrics that have a good correlation with the benefits of our approach,
such as easing new developers to onboard on projects and reuse of existing code.
This may take several months of practice and reviews.

References

[1] Apiary. *Dredd*. 2017. URL: https://github.com/apiaryio/dredd.

[2] Mario Luca Bernardi, Marta Cimitile, and Fabrizio Maria Maggi. "Automated Development of Constraint-Driven Web Applications". In: *Proceedings of the 31st Annual ACM Symposium on Applied Computing*. ACM, 2016, pp. 1196–1203.

[3] Mario Luca Bernardi et al. "M3D: A Tool for the Model Driven Development of Web Applications". In: *Proceedings of the Twelfth International Workshop on Web Information and Data Management*. WIDM 2012. Maui, HI, USA, Nov. 2, 2012, pp. 73–80.

[4] Hanyang Cao, Jean-Rémy Falleri, and Xavier Blanc. "Automated Generation of REST API Specification from Plain HTML Documentation". In: *International Conference on Service-Oriented Computing*. Springer, 2017, pp. 453–461.

[5] Marco Cremaschi and Flavio De Paoli. "Toward Automatic Semantic API Descriptions to Support Services Composition". In: *European Conference on Service-Oriented and Cloud Computing*. Springer, 2017, pp. 159–167.

[6] Frank DeRemer and Hans Kron. "Programming-in-the Large versus Programming-in-the-Small". In: *ACM Sigplan Notices*. Vol. 10. ACM, 1975, pp. 114–121.

[7] Xiang Fu, Tevfik Bultan, and Jianwen Su. "Analysis of Interacting BPEL Web Services". In: *In Proc. 13th Int. World Wide Web Conf*. Citeseer, 2004.

[8] Roy Gronmo et al. "Model-Driven Web Services Development". In: *E-Technology, e-Commerce and e-Service*. EEE'04. IEEE, 2004, pp. 42–45.

[9] Internet Engineering Task Force. *JSON Schema: A Media Type for Describing JSON Documents*. Oct. 13, 2016. URL: https://tools.ietf.org/html/draft-wright-json-schema-00.

[10] Paulo Lopes and Francesco Guardiani. *Slush-Vertx*. 2017. URL: https://github.com/pmlopes/slush-vertx.

[11] Open API Initiative. *OpenAPI Specification*. Dec. 7, 2017. URL: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md.

[12] Open API Initiative. *The Petstore Example*. Version 3.0.1. Dec. 7, 2017. URL: https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/examples/v3.0/petstore-expanded.yaml.

[13] Taylor Otwel. *Laravel*. 2016. URL: https://laravel.com/.
[14] Jack Pugaczewski et al. "Software Engineering Methodology for Development of APIs for Network Management Using the MEF LSO Framework". In: *IEEE Communications Standards* 1.1 (2017), pp. 92–96.

[15] RAML Workgroup. *RAML*. 2016. URL: https://raml.org/.

[16] Jérôme Rocheteau and David Sferruzza. "Reifier: Model-Driven Engineering of Component-Based and Service-Oriented JEE Applications". In: *ACM/IEEE 19th International Conference on Model Driven Engineering Languages and Systems*. Saint-Malo, France, Oct. 5, 2016.

[17] Markus Scheidgen, Sven Eftinge, and Frederik Marticke. "Metamodeling vs Metaprogramming: A Case Study on Developing Client Libraries for REST APIs". In: *European Conference on Modelling Foundations and Applications*. Springer, 2016, pp. 205–216.

[18] Simon Schwichtenberg, Christian Gerth, and Gregor Engels. "From Open API to Semantic Specifications and Code Adapters". In: *Web Services (ICWS), 2017 IEEE International Conference On*. IEEE, 2017, pp. 484–491.

[19] David Sferruzza. *Safe Web Services Generator*. 2017. URL: https://gitlab.startup-palace.com/research/swsg.

[20] David Sferruzza. *Specification of SWSG Extensions for OpenAPI*. 2018. URL: https://gitlab.startup-palace.com/research/swsg/tree/master/openapi-extensions-specification/1.0.0.md.

[21] David Sferruzza et al. "A Model-Driven Method for Fast Building Consistent Web Services in Practice". In: *6th International Conference on Model-Driven Engineering and Software Development*. Funchal, Madeira, Portugal, Jan. 23, 2018. URL: https://hal.archives-ouvertes.fr/hal-01654287.

[22] SmartBear Software. *Swagger Code Generator*. Version 3.0.0-rc1. May 29, 2018. URL: https://github.com/swagger-api/swagger-codegen/.

[23] SmartBear Software. *Swagger Editor*. 2018. URL: https://github.com/swagger-api/swagger-editor.

[24] SmartBear Software. *Swagger UI*. 2018. URL: https://github.com/swagger-api/swagger-UI.

[25] The PHP Group. *PHP*. 2016. URL: https://php.net/.

[26] Romanos Tsourolpis et al. "Community-Based API Builder to Manage APIs and Their Connections with Cloud-Based Services." In: *CAiSE Forum*. 2015, pp. 17–23.

[27] Wil M.P. van der Aalst, Maja Pesic, and Helen Schonenberg. "Declarative Workflows: Balancing between Flexibility and Support". In: *Computer Science-Research and Development* 23.2 (2009), pp. 99–113.

[28] Martijn van der Lee. *PHPSWaggerGen*. 2017. URL: https://github.com/vanderlee/PHPSwaggerGen.

[29] Philip Wadler. "The Essence of Functional Programming". In: *Proceedings of the 19th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages*. ACM, 1992, pp. 1–14.

[30] Egon Willighagen and Jonathan Mélius. "Automatic OpenAPI to Bio/tools Conversion". In: *bioRxiv* (2017). DOI: 10.1101/170274.