Skip to content

Asyncapi Project

DodaTech 4 min read

In this tutorial, you will learn about AsyncAPI Project: Event. We cover key concepts, practical examples, and best practices to help you master this topic.

This project brings together everything you have learned about AsyncAPI. You will design, document, generate, and deploy a complete event-driven microservice system.

Project Overview

Build an order processing platform with four Microservices communicating asynchronously via Kafka. Services include Order Service, Payment Service, Inventory Service, and Notification Service.

What You'll Build

  • Complete AsyncAPI specifications for all services
  • Kafka topic definitions with schema registry
  • Generated TypeScript clients from specs
  • Docker Compose deployment with Kafka
  • Documentation portal for all events

Why This Project

This project simulates a real-world event-driven microservice architecture. You will face the same challenges: schema design, inter-service contracts, topic configuration, and deployment complexity.

Flow Chart

flowchart TD
    A[Order Service] -->|order/created| B[Kafka]
    A -->|order/cancelled| B
    B --> C[Payment Service]
    B --> D[Inventory Service]
    B --> E[Notification Service]
    C -->|payment/authorized| B
    C -->|payment/declined| B
    D -->|inventory/reserved| B
    D -->|inventory/failed| B
    E -->|notification/sent| B

Architecture

Shared Schemas

# schemas/common.yaml
components:
  schemas:
    Money:
      type: object
      required: [amount, currency]
      properties:
        amount:
          type: number
          multipleOf: 0.01
        currency:
          type: string
          pattern: '^[A-Z]{3}$'

    Address:
      type: object
      properties:
        street: { type: string }
        city: { type: string }
        postalCode: { type: string }
        country: { type: string }

    OrderItem:
      type: object
      required: [productId, quantity, price]
      properties:
        productId: { type: string }
        quantity: { type: integer }
        price: { $ref: '#/components/schemas/Money' }

Order Service Spec

# order-service.yaml
asyncapi: '2.6.0'
info:
  title: Order Service Events
  version: '1.0.0'

servers:
  kafka:
    url: kafka://localhost:9092
    protocol: kafka

channels:
  order/created:
    publish:
      operationId: onOrderCreated
      message:
        payload:
          type: object
          required: [orderId, customerId, items, total]
          properties:
            orderId: { type: string }
            customerId: { type: string }
            items: { type: array, items: { $ref: '../schemas/common.yaml#/components/schemas/OrderItem' } }
            total: { $ref: '../schemas/common.yaml#/components/schemas/Money' }

  order/payment-processed:
    subscribe:
      operationId: orderPaymentProcessed
      message:
        payload:
          type: object
          properties:
            orderId: { type: string }
            paymentStatus: { type: string, enum: [authorized, declined] }

Generated Client Usage

// Generated from AsyncAPI spec
import { OrderServiceClient } from './generated/order-service';

const client = new OrderServiceClient({
  brokers: ['localhost:9092'],
});

// Publish order created event
await client.publishOrderCreated({
  orderId: 'ORD-123',
  customerId: 'CUST-456',
  items: [{ productId: 'PROD-1', quantity: 2, price: { amount: 29.99, currency: 'USD' } }],
  total: { amount: 59.98, currency: 'USD' },
});

// Subscribe to payment processed events
await client.onOrderPaymentProcessed(async (event) => {
  console.log(`Order ${event.orderId}: ${event.paymentStatus}`);
  if (event.paymentStatus === 'authorized') {
    await reserveInventory(event.orderId);
  }
});

Docker Compose

version: '3.8'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on: [zookeeper]
    ports:
      - "9092:9092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  schema-registry:
    image: confluentinc/cp-schema-registry:latest
    depends_on: [kafka]
    ports:
      - "8081:8081"
    environment:
      SCHEMA_REGISTRY_HOST_NAME: schema-registry
      SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka:9092

  order-service:
    build: ./services/order-service
    depends_on: [kafka, schema-registry]

  payment-service:
    build: ./services/payment-service
    depends_on: [kafka, schema-registry]

  inventory-service:
    build: ./services/inventory-service
    depends_on: [kafka, schema-registry]

  notification-service:
    build: ./services/notification-service
    depends_on: [kafka, schema-registry]

Common Mistakes

Mistake Explanation
Tight coupling through shared schemas Changes to shared schemas affect all services; version carefully and communicate changes
Skipping schema registry Without a registry, schema evolution becomes unmanageable and breaks consumers
Not testing schema compatibility Always test that new schema versions are backward compatible with existing consumers
Ignoring error channels Every service should have error/dead-letter channels documented for failed messages
Manual topic creation Use AsyncAPI tooling to automate topic provisioning from the specification

FAQ

How do I handle schema evolution across services?

Use schema registry with compatibility checking (backward, forward, or full). Version all schemas and never remove required fields.

What is the best deployment order for microservices?

Start infrastructure (Kafka, schema registry), then stateless services (order, payment), then dependent services (inventory, notification).

How do I test event-driven systems?

Use contract testing (AsyncAPI spec vs implementation), integration tests with Testcontainers, and chaos testing for resilience.

How do I monitor event flows?

Use Kafka monitoring tools (Kafka UI, Kowl), OpenTelemetry tracing across services, and metrics dashboards for event throughput.

What happens when a service is down?

Kafka retains messages based on retention policy. When the service restarts, it resumes from the last committed offset.

How do I handle exactly-once processing?

Use Kafka idempotent producers and idempotent consumers with idempotency keys in event headers.

Mini Project

Complete the full event-driven microservice implementation. Extend the system with a shipping service, a dead letter queue handler, and an event replay mechanism. Add OpenTelemetry tracing and a monitoring dashboard showing event flows between services.

What's Next

Learn about WebSocket for real-time communication

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro