The Problem: Undocumented Event Contracts
In a decoupled system, the team that produces an event and the team that consumes it may work independently. The consumer developer inevitably asks: "What fields are in the OrderPlaced event? Is it orderId or order_id? Is total a string or a number? Does items always exist?"
Without a formal contract, teams resort to Slack messages, outdated wiki pages, or reading the producer's source code. This is fragile and slows development.
The Solution: Schema Registry
EventBridge includes a Schema Registry — a central, versioned catalog of event schemas. Each schema is defined in OpenAPI 3.0 or JSONSchema Draft 4 format.
Schema Discovery (Automatic): Enable discovery on any event bus and EventBridge will analyze every event passing through it, automatically inferring and registering schemas. This is useful for bootstrapping — you get schemas without writing them manually.
# Enable schema discovery on a custom bus
aws schemas create-discoverer \
--source-arn "arn:aws:events:us-east-1:123456789012:event-bus/ecommerce-fulfillment-bus" \
--description "Auto-discover schemas from fulfillment events"
Manual Schema Registration (Recommended for Production): For production systems, treat schemas like API contracts. Register them explicitly and version them deliberately.
# Register a schema manually
aws schemas create-schema \
--registry-name "ecommerce-schemas" \
--schema-name "com.mycompany.orders@OrderPlaced" \
--type "OpenApi3" \
--content file://order-placed-schema.json
Code Bindings: The Killer Feature
Once a schema is registered, you can generate typed code bindings directly from the EventBridge console or CLI. AWS generates a class in Java, Python, or TypeScript that represents the event structure.
# Download a Python code binding
aws schemas get-code-binding-source \
--registry-name "ecommerce-schemas" \
--schema-name "com.mycompany.orders@OrderPlaced" \
--language "Python36" \
--schema-version "1" \
--output text > order_placed_event.py
In your Lambda function, instead of parsing raw JSON dictionaries and guessing field names, you import the generated class:
from order_placed_event import OrderPlaced, AWSEvent
def handler(event, context):
# Deserialize with full type safety and IDE autocomplete
aws_event: AWSEvent = AWSEvent.from_dict(event)
order: OrderPlaced = aws_event.detail
# IDE knows these fields exist and their types
print(f"Processing order {order.order_id} for ${order.total}")
Key Point: Schema Registry transforms EventBridge from a routing tool into a platform contract system. When schemas are versioned and code bindings are generated, breaking changes become visible at compile time rather than at runtime. Treat schema changes with the same discipline as API versioning — a new required field in an event is a breaking change for all consumers.