More Related Content Similar to A model based approach for developing event-driven architectures with AsyncAPI (20) A model based approach for developing event-driven architectures with AsyncAPI1. 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
8. 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
}
]
}
9. It is difficult to maintain consistency:
• Deviations in the topics:
• Lost messages
• Deviations in the message format:
• Information loss
• Runtime problems
Message-driven APIs
11. {
"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. {
"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"
}
}
}
}
}
}
13. "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"
}
}
}
}
}
}
14. "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"
}
}
}
}
}
}
15. "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"
}
}
}
}
}
}
16. "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"
}
}
}
}
}
}
17. "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"
}
}
}
}
}
}
18. "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"
}
}
}
}
}
}
19. "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"
}
}
}
}
}
}
20. "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"
}
}
}
}
}
}
25. 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
30. 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
}
]
}
31. • 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