HDI Global SE

Industry: InsuranceCustomers: 5000Revenue: 9.1 Mrd. 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, we already use OpenAPI, as such we want to ensure the same level of transparency and discoverability for asynchronous scenarios as well. Our central platform team offers the Azure Service Bus with self-service capabilities. Customers are able to manage their own topics and subscriptions by simply modifying the according values in their repositories. This means that most of the data required to document asynchronous communication is already available, although distributed between different repositories. This necessitated the creation of a comprehensive catalog of existing topics, allowing potential subscribers easy access to messages from any topics of their choosing.

Solution

The solution was to create an an AsyncAPI document where each topic owned by the customer is represented as a channel. This document is automatically generated by a pipeline whenever there's a change 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. This ensures that the information is readily accessible to every developer, allowing easy access to messages from any topics of their choosing. This approach makes our asynchronous communication is 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.
  • (Future Plans) The AsyncAPI document will be used to generate Java DTOs using the AsyncAPI Java generator, ensuring type safety and clear data structure understanding.

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 befor 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}