HDI Global SE

Industry: InsuranceCustomers: 5000Revenue: 9.1B EUR

The HDI brand operates in Germany and internationally, offering life and property/casualty insurance services. They cater to both private individuals and corporate clients, and have been providing industrial insurance since 2016.

tl;dr just go and have a look atfull production-used AsyncAPI document

HDI Global SE

Challenges

The HDI has various platform teams, among them the Integration Platform team, which offers three products: Azure API Management, Azure Event Hub, and the Azure Service Bus.
For synchronous communication OpenAPI is used as a standard. For asynchronous scenarios we want to use AsyncAPI to achieve the same level of transparency and discoverability. We as platform team offer the Azure Service Bus with self-service capabilities. Our customers are able to manage their own topics and subscriptions by maintaining a custom configuration model in a GitOps fashion. We want to establish AsyncAPI as documentation standard in HDI's organization. Information about the available message formats and topics is already available in distributed sources (e.g. repositories) and needs to be aggregated. To achieve discoverability the creation of a comprehensive catalog of existing topics is necessary, allowing potential subscribers access to information about messages from the available topics, so they can choose which ones to subscribe to.

Solution

The solution is to create AsyncAPI documents where each topic owned by the customer is represented as a channel. As we are using the GitOps setup it is straightforward to run pipeline whenever there is a change (commit) in the topic configuration. The necessary information is read from the customer repositories and then passed to a bash script as input. After successful creation, this file, along with a generated markdown for it, is saved within a documentation repository. This documentation repository serves as the basis for our Azure DevOps wiki, ensuring that all project documentation is centralized and easily accessible. As the documentation wiki is public the information is accessible to every developer, allowing easy access to messages from any topics of their choice. This approach makes our asynchronous communication as transparent and discoverable as our synchronous communication.

Use Case

  • The AsyncAPI documents are used for documentation purposes by the platform team. It provides a comprehensive overview of the asynchronous communication in our system, including the available topics and the structure of the messages.
  • (Outlook) The AsyncAPI document will be used to generate Java DTOs, ensuring type safety and clear data structure understanding.
  • (Outlook) Having the AsyncAPI documents at hand we are currently planning to use them to configure an internal developer portal (IDP) that will aggregate async and sync APIs among other things.
  • (Outlook) Evaluate if replacing the custom configuration model and use AsyncAPI documents in the center to configure asynchronous communication instead.

More Details

Languages: java, .net, bashFrameworks: Spring BootProtocols: AMQP

Testing strategy

n/a

Approach to code generation

Our team currently does not use or ofer a code generation tool. However, our customers are free to use any tool they prefer to generate DTOs from the AsyncAPI document.

Architecture

The following enterprise integration patterns are applied :

  • Message Channel Each channel in the AsyncAPI document corresponds to an existing Service Bus topic that can be subscribed to.
    1channels:
    2  claimStatus-emea:
    3    servers:
    4      - $ref: '#/servers/box-emea'
    5    address: https://namespace.servicebus.windows.net/topic/example/claimStatus
  • Message The 'messages' section under each channel in the AsyncAPI document adheres to this pattern. Each message is identified by a name and contains a payload, which represents the data transferred between applications.
    1    messages:
    2      claimStatus:
    3        name: claimStatus-Message
    4        contentType: application/json
  • Document Message The 'payload' under each message in the AsyncAPI document adheres to this pattern. The payload is a self-contained document that describes the message and is comprehensible to the message receiver.
    1payload:
    2  $ref: '#/components/schemas/claimStatus-example.json'
    In our repository structure, the message-schema file is stored separately and just referenced in the Topic configs. To make sure that all information is accessible in one place, the schema is directly copied to the AsyncAPI document.
    1components:
    2  schemas:
    3      claimStatus-example.json: {
    4        "$schema": "http://json-schema.org/draft-06/schema#",
    5        "type": "object",
    6        "properties": {
    7          "message": {
    8            "type": "object",
    9            "properties": {
    10              "version": {
    11                "type": "string"
    12              },
    13              "header": {
    14                "type": "object",
    15                "properties": {
    16                  "messageId": {
    17                    "type": "string"
    18                  },
    19                  "entityType": {
    20                    "type": "string"
    21                  },
    22                  "eventType": {
    23                    "type": "object",
    24                    "enum": [
    25                      "create",
    26                      "update"
    27                    ]
    28                  }
    29                },
    30                "required": [
    31                  "messageId",
    32                  "entityType",
    33                  "eventType"
    34                ]
    35              },
    36              "data": {
    37                "type": "object",
    38                "properties": {
    39                  "par": {
    40                    "type": "object",
    41                    "properties": {
    42                      "tenantNumber": {
    43                        "type": "string"
    44                      },
    45                      "policyNumber": {
    46                        "type": "number"
    47                      },
    48                      "contractNumber": {
    49                        "type": "number"
    50                      },
    51                      "sourceSystem": {
    52                        "type": "string"
    53                      },
    54                      "claimStatus": {
    55                        "type": "string"
    56                      },
    57                      "currencyCode": {
    58                        "type": "string"
    59                      },
    60                      "registrYear": {
    61                        "type": "number"
    62                      },
    63                      "broker": {
    64                        "type": "string"
    65                      },
    66                      "insured": {
    67                        "type": "string"
    68                      },
    69                      "lineOfBusiness": {
    70                        "type": "number"
    71                      },
    72                      "claimCountry": {
    73                        "type": "string"
    74                      },
    75                      "isNewClaim": {
    76                        "type": "boolean"
    77                      },
    78                      "dateOfLoss": {
    79                        "type": "string",
    80                        "format": "date"
    81                      },
    82                      "creationDate": {
    83                        "type": "string",
    84                        "format": "date"
    85                      },
    86                      "notificationDate": {
    87                        "type": "string",
    88                        "format": "date"
    89                      },
    90                      "businessDate": {
    91                        "type": "string",
    92                        "format": "date"
    93                      }
    94                    },
    95                    "required": [
    96                      "tenantNumber"
    97                    ]
    98                  }
    99                },
    100                "required": [
    101                  "par"
    102                ]
    103              }
    104            },
    105            "required": [
    106              "version",
    107              "header",
    108              "data"
    109            ]
    110          }
    111        },
    112        "required": [
    113          "message"
    114        ]
    115      }
    116

More Details about AsyncAPI

Version: 3.0.0Who maintains documents: Customers maintain this document automatically by maintaining their own topic configurations. Internal users: trueExternal users: false

How AsyncAPI documents are stored

A seperate Git repository functions as a "customer Wiki", serving as a central location for all relevant documents about the existing infrastructure. This includes the AsyncAPI document, which provides a comprehensive overview of the asynchronous communication in our system.

Where maintainers edit AsyncAPI documents

If changes for any topic are applied by a customer, the documentation pipeline is triggered. This pipeline uses a bash script to read the updated configuration from the customer's repository. It then generates a new AsyncAPI document reflecting these changes.

The AsyncAPI document is validated using the AsyncAPI CLI to confirm that the document is correctly formatted and adheres to the AsyncAPI specification right after creation and before being commited to the documentation repository.

What extensions are used

none

How documentation is generated

Documentation is generated via AsyncAPI CLI and published to the Azure DevOps wiki right after the AsyncAPI file is generated.

What bindings are used

none

Schemas

Spec: JSON Schema

Storage strategy

A Git repository functions as a self-service portal where customers can manage their own configurations for the Azure Service Bus. This includes defining their own message schemas for any topics they own.

Schema Registry

none

Versioning of schemas

The customer has the freedom to choose the versioning for their own message-schema files.

Validation of message schemas

Validation using Ajv is used solely for the purpose of ensuring that the JSON data adheres to the expected schema. This validation is performed for each customer by the Pull Request pipeline we provide. It does not perform any other form of validation or processing on the data.

Additional Resources

Production-use AsyncAPI document

1asyncapi: 3.0.0
2info:
3  title: customer-example
4  version: 1.0.0
5  description: |
6    This is an AsyncAPI document for customer-example.
7    It contains every Topic owned by the customer in form of channel. 
8    Dowload the coresponding schema files from the following link:
9servers:
10  box-apac:
11    host: namespace.servicebus.windows.net
12    protocol: amqp
13    description: Azure Service Bus namespace endpoint for box.
14  box-emea:
15    host: namespace.servicebus.windows.net
16    protocol: amqp
17    description: Azure Service Bus namespace endpoint for box.
18channels:
19  claimStatus-emea:
20    servers:
21      - $ref: '#/servers/box-emea'
22    address: https://namespace.servicebus.windows.net/topic/example/claimStatus
23    messages:
24      claimStatus:
25        name: claimStatus-Message
26        contentType: application/json
27        payload:
28          $ref: '#/components/schemas/claimStatus-example.json'
29  claimDetails-emea:
30    servers:
31      - $ref: '#/servers/box-emea'
32    address: https://namespace.servicebus.windows.net/topic/example/claimDetails
33    messages:
34      claimDetails:
35        name: claimDetails-Message
36        contentType: application/json
37        payload:
38          $ref: '#/components/schemas/claimDetails-example.json'
39operations:
40  claimStatus-emea:
41    action: send
42    channel:
43      $ref: '#/channels/claimStatus-emea'
44    messages:
45      - $ref: '#/channels/claimStatus-emea/messages/claimStatus'
46  claimDetails-emea:
47    action: send
48    channel:
49      $ref: '#/channels/claimDetails-emea'
50    messages:
51      - $ref: '#/channels/claimDetails-emea/messages/claimDetails'
52components:
53  schemas:
54      claimDetails-example.json: {
55  "$schema": "http://json-schema.org/draft-06/schema#",
56  "type": "object",
57  "properties": {
58    "policyNumber": {
59      "type": "string"
60    },
61    "claimNumber": {
62      "type": "string"
63    },
64    "notificationDate": {
65      "type": "string"
66    },
67    "occurrenceDate": {
68      "type": "string"
69    },
70    "claimAmount": {
71      "type": "integer"
72    },
73    "description": {
74      "type": "string"
75    },
76    "editor": {
77      "type": "string"
78    },
79    "location": {
80      "type": "string"
81    },
82    "country": {
83      "type": "string"
84    },
85    "currency": {
86      "type": "string"
87    },
88    "movements": {
89      "type": "array",
90      "items": [
91        {
92          "type": "object",
93          "properties": {
94            "amount": {
95              "type": "integer"
96            },
97            "movementType": {
98              "type": "string"
99            },
100            "benefitType": {
101              "type": "string"
102            }
103          },
104          "required": [
105            "amount",
106            "movementType",
107            "benefitType"
108          ]
109        },
110        {
111          "type": "object",
112          "properties": {
113            "amount": {
114              "type": "integer"
115            },
116            "movementType": {
117              "type": "string"
118            },
119            "benefitType": {
120              "type": "string"
121            }
122          },
123          "required": [
124            "amount",
125            "movementType",
126            "benefitType"
127          ]
128        },
129        {
130          "type": "object",
131          "properties": {
132            "amount": {
133              "type": "integer"
134            },
135            "movementType": {
136              "type": "string"
137            },
138            "benefitType": {
139              "type": "string"
140            }
141          },
142          "required": [
143            "amount",
144            "movementType",
145            "benefitType"
146          ]
147        }
148      ]
149    }
150  },
151  "required": [
152    "policyNumber",
153    "claimNumber"
154  ]
155}
156      claimStatus-example.json: {
157  "$schema": "http://json-schema.org/draft-06/schema#",
158  "type": "object",
159  "properties": {
160    "message": {
161      "type": "object",
162      "properties": {
163        "version": {
164          "type": "string"
165        },
166        "header": {
167          "type": "object",
168          "properties": {
169            "messageId": {
170              "type": "string"
171            },
172            "entityType": {
173              "type": "string"
174            },
175            "eventType": {
176              "type": "object",
177              "enum": [
178                "create",
179                "update"
180              ]
181            }
182          },
183          "required": [
184            "messageId",
185            "entityType",
186            "eventType"
187          ]
188        },
189        "data": {
190          "type": "object",
191          "properties": {
192            "par": {
193              "type": "object",
194              "properties": {
195                "tenantNumber": {
196                  "type": "string"
197                },
198                "policyNumber": {
199                  "type": "number"
200                },
201                "contractNumber": {
202                  "type": "number"
203                },
204                "sourceSystem": {
205                  "type": "string"
206                },
207                "claimStatus": {
208                  "type": "string"
209                },
210                "currencyCode": {
211                  "type": "string"
212                },
213                "registrYear": {
214                  "type": "number"
215                },
216                "broker": {
217                  "type": "string"
218                },
219                "insured": {
220                  "type": "string"
221                },
222                "lineOfBusiness": {
223                  "type": "number"
224                },
225                "claimCountry": {
226                  "type": "string"
227                },
228                "isNewClaim": {
229                  "type": "boolean"
230                },
231                "dateOfLoss": {
232                  "type": "string",
233                  "format": "date"
234                },
235                "creationDate": {
236                  "type": "string",
237                  "format": "date"
238                },
239                "notificationDate": {
240                  "type": "string",
241                  "format": "date"
242                },
243                "businessDate": {
244                  "type": "string",
245                  "format": "date"
246                }
247              },
248              "required": [
249                "tenantNumber"
250              ]
251            }
252          },
253          "required": [
254            "par"
255          ]
256        }
257      },
258      "required": [
259        "version",
260        "header",
261        "data"
262      ]
263    }
264  },
265  "required": [
266    "message"
267  ]
268}