A3S Docs
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:

  1. Schema validation (on plaintext payload)
  2. Encryption (payload becomes ciphertext)
  3. Publish to provider

This ensures schemas always validate the actual data, not encrypted blobs.

On this page