Deepen PracticeOrdered learning track

AsyncAPI, Event Contract Testing, and Governance

Learn Java Microservices Communication - Part 078

AsyncAPI, event contract testing, and governance for Java microservices: event catalogs, producer/consumer contracts, schema compatibility, fixtures, Spring Kafka/Testcontainers integration tests, CI gates, review workflows, and platform policy.

5 min read847 words
PrevNext
Lesson 7896 lesson track53–79 Deepen Practice
#java#microservices#communication#asyncapi+6 more

Part 078 — AsyncAPI, Event Contract Testing, and Governance

Synchronous APIs usually have explicit contracts.

HTTP APIs have OpenAPI.

gRPC APIs have .proto.

Event-driven APIs often have something weaker:

topic name + tribal knowledge + sample JSON in a wiki

That is not enough.

A topic is an API.

An event type is an API operation.

A message schema is an API payload.

A producer is an API provider.

A consumer is an API client.

If event contracts are not documented, tested, and governed, async systems drift until an incident reveals hidden coupling.


1. Async API Mental Model

An asynchronous API contract should describe:

  • channels/topics,
  • messages,
  • producers,
  • consumers,
  • payload schemas,
  • headers,
  • keys,
  • protocol bindings,
  • security,
  • versioning,
  • compatibility,
  • examples,
  • ownership,
  • operational expectations.

AsyncAPI is a specification for describing message-driven APIs in machine-readable form.

For Java/Kafka teams, AsyncAPI can become the event equivalent of OpenAPI.


2. AsyncAPI vs Schema Registry

Schema registry stores payload schemas and compatibility metadata.

AsyncAPI describes the API/channel contract.

ConcernSchema RegistryAsyncAPI
payload schemastrongreferences/includes
compatibility checksstrongcan reference
channel/topic docsweakstrong
producer/consumer rolesweakstrong
exampleslimitedstrong
protocol bindingslimitedstrong
security docslimitedstrong
human API catalogweakstrong

Use both.

Schema registry does not document business semantics by itself.


3. Event Contract Contents

A production event contract includes:

eventType: com.example.case.CaseEscalated.v1
topic: case-events
owner: case-platform
producer: case-service
key:
  field: caseId
  purpose: per-case ordering
payloadSchema: case-escalated-v1
headers:
  required:
    - event_id
    - event_type
    - correlation_id
    - causation_id
compatibility: full-transitive
retention: 7d
classification: internal-confidential
replaySafe: true
consumers:
  - notification-service
  - search-indexer
  - audit-service

Contract is operational design.

Not only JSON shape.


4. AsyncAPI Skeleton

asyncapi: 3.0.0
info:
  title: Case Events API
  version: 1.0.0

channels:
  caseEvents:
    address: case-events
    messages:
      CaseEscalated:
        $ref: '#/components/messages/CaseEscalated'

operations:
  publishCaseEscalated:
    action: send
    channel:
      $ref: '#/channels/caseEvents'
    messages:
      - $ref: '#/channels/caseEvents/messages/CaseEscalated'

components:
  messages:
    CaseEscalated:
      name: CaseEscalated
      contentType: application/json
      payload:
        $ref: '#/components/schemas/CaseEscalatedPayload'

The important model:

channel + message + schema + operation + metadata

5. Event Catalog

An event catalog is a searchable inventory of events/topics.

For each event:

  • name,
  • description,
  • owner,
  • producer,
  • topic,
  • schema,
  • examples,
  • key policy,
  • version,
  • compatibility,
  • consumers,
  • retention,
  • classification,
  • SLOs,
  • replay policy,
  • DLQ policy,
  • contact/runbook.

Event catalog improves discovery, review, onboarding, and incident response.


6. Producer Contract Tests

Producer contract test verifies:

given domain change
producer emits expected event contract

Test:

  • topic,
  • key,
  • event type,
  • event ID,
  • headers,
  • payload schema,
  • absence of forbidden data,
  • aggregate version.

Example:

@Test
void escalationEmitsCaseEscalatedEventContract() {
    useCase.execute(command);

    OutboxMessage event = outboxRepository.singlePending();

    assertThat(event.topic()).isEqualTo("case-events");
    assertThat(event.messageKey()).isEqualTo("CASE-100");
    assertThat(event.eventType()).isEqualTo("CaseEscalated.v1");

    schemaValidator.validate(event.payload(), "case-escalated-v1");
}

This catches contract drift at source.


7. Consumer Contract Tests

Consumer contract test verifies:

given valid fixture
consumer handles it correctly

Example:

@Test
void consumesCaseEscalatedV1Fixture() {
    EventEnvelope envelope = fixture("case-escalated/v1/valid.json");

    consumer.handle(envelope);

    assertThat(repository.get("CASE-100").status())
        .isEqualTo("ESCALATED");
}

Also test:

  • unknown fields,
  • missing optional field,
  • unknown enum,
  • old schema version,
  • duplicate,
  • out-of-order version,
  • invalid schema.

Consumer compatibility depends on real fixtures.


8. Fixture Strategy

Store fixtures:

src/test/resources/contracts/events/
  case-escalated/v1/
    valid-minimal.json
    valid-full.json
    unknown-extra-field.json
    unknown-enum.json
    missing-required-case-id.json

Fixtures should be stable, reviewed, versioned, and used in docs and CI.

Fixtures are executable examples.


9. Schema Compatibility Gate

CI should check:

  • new schema vs previous schema,
  • transitive compatibility,
  • forbidden field names,
  • required field changes,
  • enum changes,
  • removed fields/reserved numbers,
  • subject naming policy.

Schema compatibility is necessary but not sufficient.

Semantic compatibility still needs review.


10. Semantic Compatibility

Schema may pass while meaning breaks.

Example:

field remains string
but meaning changes from case status to escalation status

Consumers break.

Review:

  • field meaning,
  • key changes,
  • event type changes,
  • ordering,
  • timestamp semantics,
  • replay behavior,
  • retention,
  • privacy classification,
  • new PII fields.

Human API review complements automated checks.


11. Consumer-Driven Contracts

Consumers can publish requirements.

consumer: notification-service
eventType: CaseEscalated.v1
requires:
  fields:
    - caseId
    - escalationId
    - targetQueue
  headers:
    - correlation_id
unknownFields: ignore
replaySafe: false

Producer checks impact before changes.

This reveals hidden dependencies.


12. Spring Kafka and Testcontainers

Use Spring Kafka tests for:

  • listener container behavior,
  • serialization/deserialization,
  • error handler,
  • DLT,
  • retry topics,
  • headers.

Use Testcontainers for:

  • real broker integration,
  • schema registry,
  • producer/consumer round trip,
  • Kafka Streams,
  • outbox relay.

Mocks are not enough for protocol and container behavior.


13. CI Gate Template

asyncApiCi:
  required:
    - asyncapi-lint
    - schema-compatibility
    - producer-contract-tests
    - consumer-fixture-tests
    - header-key-tests
    - forbidden-field-scan
    - historical-replay-fixtures
    - documentation-generation

  breakingChange:
    requires:
      - architecture-review
      - consumer-impact-list
      - migration-plan
      - sunset-date
      - dual-publish-plan

Automate what machines can check.

Review semantics humans must judge.


14. Breaking Change Process

Breaking event change requires migration:

  1. create new event major version,
  2. publish both versions or new topic,
  3. update AsyncAPI docs,
  4. notify consumers,
  5. provide fixtures,
  6. migrate consumers,
  7. monitor old version usage,
  8. stop old version after retention/replay window,
  9. archive old docs/fixtures.

Breaking changes are programs, not quick PRs.


15. Event Review Checklist

Ask:

  • Is event a fact or command?
  • Is owner clear?
  • Is topic correct?
  • Is key correct?
  • Is ordering scope documented?
  • Is payload minimal?
  • Is PII included?
  • Is schema compatible?
  • Are examples present?
  • Are old consumers safe?
  • Are replay semantics defined?
  • Are producer/consumer tests updated?
  • Is AsyncAPI updated?

Event review is API review.


16. The Real Lesson

Async APIs need contracts as much as synchronous APIs.

A topic is durable, replayable, shared, and consumed by many services.

Production-grade async governance requires:

AsyncAPI/event catalog
+ schema compatibility
+ fixtures
+ producer contract tests
+ consumer contract tests
+ semantic review
+ privacy review
+ CI gates
+ drift detection

That is how event-driven architecture scales across teams without becoming event-driven chaos.


References

Lesson Recap

You just completed lesson 78 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.