Using AsyncAPI in event-driven architecture

Empowering event-driven application development using contract-first development, AsyncAPI and event-driven architecture.

In the ever-evolving landscape of software architecture, event-driven architecture (EDA) has risen to prominence as a catalyst for building scalable and responsive systems. This architectural shift is further enhanced by contract-first development, a methodology that takes center stage when paired with AsyncAPI. This article explores how the synergy between contract-first development, AsyncAPI and EDA empowers application development. A small code example is shared to illustrate these concepts in action.

Contract-first development: a prerequisite for success

A contract-first development approach places the API contract or specification at the forefront of development. This contract is a formal document that outlines the structure, endpoints, data formats and expected behaviors of the API. By defining the contract before writing any code, you set a clear direction for your development efforts, fostering collaboration and reducing integration complexities.

How contract-first development and event-driven architecture work together

EDA is a design paradigm that hinges on the concept of events and real-time communication. Events represent significant occurrences in your system, and components react to these events asynchronously. EDA promotes loose coupling, scalability and real-time responsiveness, making it an ideal choice for many modern applications.

Here’s how contract-first development intertwines with AsyncAPI in the realm of EDA:

  • AsyncAPI specification: This encapsulates the contract for your asynchronous API in the context of EDA (typically in YAML or JSON). It includes event topics, message structures, channels and other crucial details.
  • Team collaboration: Share the AsyncAPI specification with your development teams, including front-end, back-end and other stakeholders. This shared contract facilitates parallel work, ensuring alignment regarding event structures and behaviors.
  • Implementation: Developers can then commence building the event-driven components based on the generated code templates. This approach minimizes discrepancies between the contract and the actual implementation, ensuring a smooth integration process.
  • Testing: It aids in testing and validation, ensuring the reliability and integrity of messages.
  • Event-driven messaging: With an AsyncAPI-based contract and generated code, you can confidently implement event-driven messaging. Producers emit events that conform to the contract, and consumers receive and process them consistently.

Defining AsyncAPI concepts

The AsyncAPI schema facilitates the delineation of various components integral to the EDA framework. These components include:

  • Server: This entity acts as a messaging broker system, enabling the establishment of connections and facilitating communication between a producer and a consumer.

  • Producer: Defined as an application that detects changes in state (events) and disseminates these events as messages. An event is characterized by a change or update in state, instigated by an action from a user or device.

  • Consumer: This refers to an application that monitors for specific events from a broker and responds accordingly.

  • Channel: A construct devised by the server to categorize and dispatch messages. Channels can be designated as topics, queues, routing keys, paths or subjects, contingent on the protocol employed.

  • Application: Must assume the role of either a producer, a consumer or both.

  • Protocol: Constitutes a collection of guidelines that dictate the mode of data transmission between applications and/or servers. Examples of protocols include WebSockets, HTTP, Kafka and MQTT.

  • Message: Serves as the conduit for information exchange between a sender and a receiver via channels. A message, which may also be referred to as an event or a command, is capable of being consumed by multiple independent receivers. It encompasses a payload of data that necessitates processing and serialization into a suitable format (e.g., JSON, XML, binary) by the receiver. Additionally, it may contain metadata—referred to as headers or properties—that elucidates the characteristics of the message itself.

The AsyncAPI schema helps describe the following entities as part of the EDA solution:

  • Server: represents a messaging broker system where connections and communication between a producer and a consumer are established.

  • Producer: is an application that senses state changes (events) and publishes those events as messages. An event indicates a state change or update triggered by a user’s/device’s action.

  • Consumer: is an application that listens for a particular event from a broker and reacts to it.

  • Channel: is a mechanism created by the server to organize and transmit messages. Users can define channels as a topic, queue, routing key, path or subject depending on the protocol used.

  • Application: must be a producer, a consumer or both.

  • Protocol: is a set of rules that specifies how information is exchanged between applications and/or servers. Protocol examples:

  • Message: is a communication asset used to transmit or exchange information from a sender to the receiver through channels. Multiple independent receivers can consume a single message, which can also be defined as an event or command. The sender includes a payload of data that needs to be processed and serialized by the receiver into an appropriate format, such as JSON, XML, binary, etc. It may also include metadata, known as headers or properties, which describe the message itself. 

AsyncAPI schema example

Let’s check the below schema example:

      schemas:
 lightMeasured:
   type: object
   properties:
     id:
       type: integer
       minimum: 0
       description: Id of the streetlight.
     lumens:
       type: integer
       minimum: 0
       description: Light intensity measured in lumens.
     sentAt:
       type: string
       format: date-time
       description: Date and time when the message was sent.
 channels:
   light/measured:
     bindings:
       amqp:
         is: routingKey
         queue:
           name: light/measured
           durable: true
           exclusive: true
           autoDelete: false
           vhost: /
         bindingVersion: 0.2.0
     publish:
       summary: Inform about environmental lighting conditions for a particular streetlight.
       operationId: onLightMeasured
       message:
         $ref: '#/components/messages/lightMeasured'
     subscribe:
       summary: publishes light measurements
       operationId: publishLumens
       message:
         $ref: '#/components/messages/lightMeasured'
    

The schema describes the EDA in a YAML file that allows us to understand the interaction between the architecture components.

Employing AsyncAPI generators

Once the schema is defined, it is possible to generate the boilerplate code for producer and consumers using one of the AsyncAPI generators.

Below are examples of various AsyncAPI generators:

Template Name

Description

Source code

@asyncapi/nodejs-template

Generates Nodejs service that uses Hermes package

click here

@asyncapi/nodejs-ws-template

Generates Nodejs service that supports WebSockets protocol only

click here

@asyncapi/java-template

Generates Java JMS application

click here

@asyncapi/java-spring-template

Generates Java Spring service

click here

@asyncapi/java-spring-cloud-stream-template

Generates Java Spring Cloud Stream service

click here

@asyncapi/python-paho-template

Generates Python service that uses Paho library

click here

@asyncapi/html-template

Generates HTML documentation site

click here

@asyncapi/markdown-template

Generates documentation in Markdown file

click here

@asyncapi/ts-nats-template

Generates TypeScript NATS client

click here

@asyncapi/go-watermill-template

Generates Go client using Watermill

click here

@asyncapi/dotnet-nats-template

Generates .NET C# client using NATS

click here

@asyncapi/php-template

Generates PHP client using RabbitMQ

click here

@asyncapi/dotnet-rabbitmq-template

Generates .NET C# client using RabbitMQ

click here

 

GO Example:
Requirements

      ➜ asyncapi-generator git:(main) npm install @async/go-watermill-template
    

Boilerplate code generation:

      ➜  asyncapi-generator git:(main) ag asyncapi.yaml @asyncapi/go-watermill-template -o ./async-api-example -p moduleName=async-api-example
Done! ✨
Check out your shiny new generated files at /Users/sergio/Documents/repositories/asyncapi-generator/async-api-example.
    

Working with AsyncAPI Studio

AsyncAPI Studio is a robust development and collaboration platform designed specifically for working with AsyncAPI, the specification for asynchronous APIs. It serves as a centralized hub for teams to design, document, and manage their event-driven architectures. AsyncAPI Studio streamlines the creation of API definitions, enabling developers to visually design schemas and, channels, preview documentation, and generate templates for AsyncAPI specifications.

Image of the AsyncAPI Studio website

Key takeaways for successful application development

In the dynamic landscape of software development, harnessing the synergy between contract-first development, AsyncAPI and EDA is key to building robust, scalable and responsive applications. These principles foster collaboration, reduce errors and ensure that your event contracts and coordinations are adhered to throughout the development process. By embracing these concepts and practices, your event-driven applications will be well equipped to thrive in the era of real-time communication and complex business processes. 

Happy coding!

Learn more about tech at Capital One

New to tech at Capital One?


Sergio Bilello , Distinguished Software Engineer

A visionary engineering leader with more than 15 years of experience setting the strategy for highly available, resilient and scalable infrastructure for organizations serving millions of users. Proven track record in building and leading world-class teams to deliver complex, multiyear infrastructure projects from concept to execution. Expert in traffic management, capacity planning and performance engineering, with hands-on experience designing systems for traffic shaping, load testing and cluster management at scale. An exceptional mentor dedicated to hiring, developing and partnering with high-potential and principal-level engineers.