A3S Event
Schema Validation
Event schemas with required fields validation and version compatibility checking
Schema Validation
A3S Event supports optional schema validation on publish. Register event schemas with required fields, and the EventBus validates payloads before they reach the provider.
Enable Schema Validation
use a3s_event::{EventBus, MemoryProvider, MemorySchemaRegistry, EventSchema};
use std::sync::Arc;
let registry = MemorySchemaRegistry::new();
registry.register(EventSchema {
event_type: "order.created".into(),
version: 1,
required_fields: vec!["order_id".into(), "total".into()],
description: "Order creation event".into(),
})?;
registry.register(EventSchema {
event_type: "order.shipped".into(),
version: 1,
required_fields: vec!["order_id".into(), "tracking_number".into()],
description: "Order shipped event".into(),
})?;
let bus = EventBus::with_schema_registry(MemoryProvider::default(), Arc::new(registry));Validation on Publish
When a schema registry is set, typed events are validated against the registered schema:
use a3s_event::Event;
// Passes validation (has order_id and total)
let event = Event::typed(
"events.orders.created", "orders",
"order.created", 1,
"New order ORD-001", "order-service",
serde_json::json!({ "order_id": "ORD-001", "total": 99.99 }),
);
bus.publish_event(&event).await?;
// Fails validation (missing "total" field)
let bad_event = Event::typed(
"events.orders.created", "orders",
"order.created", 1,
"New order ORD-002", "order-service",
serde_json::json!({ "order_id": "ORD-002" }),
);
assert!(bus.publish_event(&bad_event).await.is_err());Events created with Event::new() have an empty event_type and skip schema validation.
Schema Evolution
Register multiple versions of the same event type:
// Version 1: original schema
registry.register(EventSchema {
event_type: "order.created".into(),
version: 1,
required_fields: vec!["order_id".into(), "total".into()],
description: "Initial schema".into(),
})?;
// Version 2: added "currency" field
registry.register(EventSchema {
event_type: "order.created".into(),
version: 2,
required_fields: vec!["order_id".into(), "total".into(), "currency".into()],
description: "Added currency field".into(),
})?;Compatibility Checking
Check whether a new schema version is compatible with an existing one:
use a3s_event::Compatibility;
let compat = registry.check_compatibility("order.created", 1, 2)?;
match compat {
Compatibility::Backward => println!("New version can read old data"),
Compatibility::Forward => println!("Old version can read new data"),
Compatibility::Full => println!("Fully compatible in both directions"),
Compatibility::None => println!("Breaking change"),
}Compatibility Rules
Prop
Type
SchemaRegistry Trait
Implement custom registries (e.g., backed by a database):
use a3s_event::{SchemaRegistry, EventSchema, Compatibility, Event};
use a3s_event::Result;
pub trait SchemaRegistry: Send + Sync {
/// Register a schema for an event type at a specific version
fn register(&self, schema: EventSchema) -> Result<()>;
/// Get the schema for an event type at a specific version
fn get(&self, event_type: &str, version: u32) -> Result<Option<EventSchema>>;
/// Get the latest schema version for an event type
fn latest_version(&self, event_type: &str) -> Result<Option<u32>>;
/// List all registered event types
fn list_types(&self) -> Result<Vec<String>>;
/// Validate an event's payload against its registered schema
fn validate(&self, event: &Event) -> Result<()>;
/// Check compatibility between two schema versions
fn check_compatibility(
&self, event_type: &str, old_version: u32, new_version: u32,
) -> Result<Compatibility>;
}EventSchema Fields
Prop
Type
Querying the Registry
// Get a specific schema
let schema = registry.get("order.created", 1)?;
// Get the latest version number
let latest = registry.latest_version("order.created")?;
// List all registered event types
let types = registry.list_types()?;Validation Order
When both schema validation and encryption are enabled:
- Schema validation (on plaintext payload)
- Encryption (payload becomes ciphertext)
- Publish to provider
This ensures schemas always validate the actual data, not encrypted blobs.