Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A model based approach for developing event-driven architectures with AsyncAPI

47 views

Published on

In this Internet of Things (IoT) era, our everyday objects have evolved into the so-called cyber-physical systems (CPS).
The use and deployment of CPS has especially penetrated the industry, giving rise to the Industry 4.0 or Industrial IoT (IIoT).
Typically, architectures in IIoT environments are distributed and asynchronous, communication being guided by events such as the publication of (and corresponding subscription to) messages.

While these architectures have some clear advantages (such as scalability and flexibility), they also raise interoperability challenges among the agents in the network. Indeed, the knowledge about the message content and its categorization (topics) gets diluted, leading to consistency problems, potential losses of information and complex processing requirements on the subscriber side to try to understand the received messages.


In this paper, we present our proposal relying on \emph{AsyncAPI} to automate the design and implementation of these architectures using model-based techniques for the generation of (part of) event-driven infrastructures.
The prototype that implements this proposal as an open-source project is available at https://github.com/SOM-Research/asyncapi-toolkit

Published in: Software
  • Be the first to comment

  • Be the first to like this

A model based approach for developing event-driven architectures with AsyncAPI

  1. 1. A model-based approach for developing event-driven architectures with AsyncAPI Abel Gómez1, Markel Iglesias-Urkia2, Aitor Urbieta2, Jordi Cabot1,3 1Universitat Oberta de Catalunya, 2Ikerlan, 3ICREA MODELS conference – April 22, 2020
  2. 2. Subscribe! Publish! Broker
  3. 3. An illustrative example
  4. 4. An illustrative example
  5. 5. Iotbox/box1/monitor An illustrative example { "id": "Line A", "presses":[ { "id": "Press A1", "ts": "2020-05-11T18:01:06.158Z", "value": 10.0 },{ "id": "Press A2", "ts": "2020-05-11T18:01:06.329Z", "value": 15.5 } ] }
  6. 6. An illustrative example
  7. 7. An illustrative example Iotbox/box1/monitoring { "id": "Line A", "Press A1": { "ts": "2020-05-11T18:01:06.158Z", "value": 10.0 }, "Press A2“: { "ts": "2020-05-11T18:01:06.329Z", "value": 15.5 } ] } Iotbox/box1/monitor { "id": "Line A", "presses":[ { "id": "Press A1", "ts": "2020-05-11T18:01:06.158Z", "value": 10.0 },{ "id": "Press A2", "ts": "2020-05-11T18:01:06.329Z", "value": 15.5 } ] }
  8. 8. It is difficult to maintain consistency: • Deviations in the topics: • Lost messages • Deviations in the message format: • Information loss • Runtime problems Message-driven APIs
  9. 9. { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  10. 10. { "asyncapi": "2.0.0", "info": { … }, "servers": { … }, "channels": { … }, "components": { … } } { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  11. 11. "info": { "title": "IoTBox API", "version": "1.0.0" }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  12. 12. "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  13. 13. "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": ..., "schema": { "type": "string" } } }, "publish": { ... }, "subscribe": { ... } } }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  14. 14. "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  15. 15. "components": { "messages": { … }, "schemas": { … } } { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  16. 16. "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  17. 17. "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo“ } } } }, { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  18. 18. "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } { "asyncapi": "2.0.0", "info": { "title": "IoTBox API", "version": "1.0.0”}, "servers": { "production": { "protocol": "mqtt", "url": "example.com:1883" } }, "channels": { "iotbox/{id}/monitor": { "parameters": { "id": { "description": "The ID of the IoTBox", "schema": { "type": "string" } } }, "publish": { "operationId": "publishStatus", "message": { "$ref": "#/components/messages/statusMessage" } }, "subscribe": { "operationId": "subscribeStatus", "message": { "$ref": "#/components/messages/statusMessage" } } } }, "components": { "messages": { "statusMessage": { "description": "Status of a given subsystem", "payload": { "$ref": "#/components/schemas/lineInfo" } } }, "schemas": { "lineInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the subsystem" }, "presses": { "type": "array", "description": "Info of presses in this subsystem", "items": { "$ref": "#/components/schemas/pressInfo" } } } }, "pressInfo": { "type": "object", "properties": { "id": { "type": "string", "description": "Identifier of the press" }, "ts": { "type": "string", "title": "Timestamp" }, "value": { "type": "number", "description": "Pressure of the press in Pascals" } } } } } }
  19. 19. AsyncAPI toolkit A model-based approach for developing event-driven architectures with AsyncAPI
  20. 20. AsyncAPI toolkit
  21. 21. A grammar for the AsyncAPI specification
  22. 22. AsyncAPI: {AsyncAPI} '{‘ ( ( '"asyncapi"' ':' version=VersionNumber ','? ) & ( '"info"' ':' info=Info ','? ) & ( '"servers"' ':' '{' servers+=Server (',' servers+=Server)* '}' ','? )? & ( '"channels"' ':' '{' channels+=Channel (',' channels+=Channel)* '}' ','? )? & ( '"components"' ':' components=Components ','? )? ) '}'; Info: {Info} '{' ( ( '"title"' ':' title=AnyString ','? ) & ( '"version"' ':' version=AnyString ','? ) & ( '"description"' ':' description=AnyString ','? )? & ( '"termsOfService"' ':' termsOfService=AnyString ','? )? & ( '"contact"' ':' contact=Contact ','? )? & ( '"license"' ':' license=License ','? )? ) '}'; A grammar for the AsyncAPI specification
  23. 23. A metamodel for the AsyncAPI specification
  24. 24. Defining an event-driven architecture
  25. 25. Equivalent API model
  26. 26. The generated code
  27. 27. The generated code package main; import java.time.Instant; import iotbox._id_.monitor.PublishStatus; import iotbox._id_.monitor.PublishStatus.PublishStatusParams; import schemas.LineInfo; import schemas.PressInfo; public class Publish { public static void main(String[] args) throws Exception { LineInfo payload = LineInfo.newBuilder() .withId("Line A") .addToPresses( PressInfo.newBuilder() .withId("Press A1") .withTimestamp(Instant.now().toString()) .withValue(10.0) .build() ) .addToPresses( PressInfo.newBuilder() .withId("Press A2") .withTimestamp(Instant.now().toString()) .withValue(15.5) .build() ) .build(); PublishStatusParams params = PublishStatusParams.create().withId("box1"); PublishStatus.publish(payload, params); } } { "id": "Line A", "presses": [ { "id": "Press A1", "ts": "2020-05-11T18:01:06.158Z", "value": 10.0 },{ "id": "Press A2", "ts": "2020-05-11T18:01:06.329Z", "value": 15.5 } ] }
  28. 28. • With AsyncAPI Toolkit, an AsyncAPI specification is the singlesource of truthwhen designing and running an event-driven architecture. • The AsyncAPI Toolkit leverages model-based techniques for: • lower development and deployment time; • better code quality; • increased maintanability • better requirements management; • better documentation. Summary and conclusions We see AsyncAPI Toolkit as a toolkit able to support most phasesof the development cycle, from requirementsto code-generation to —in the future— testing. —Ikerlan
  29. 29. UOCresearch @UOC_research https://github.com/SOM-Research/asyncapi-toolkit agomezlla@uoc.edu

×