Skip to main content
Version: Next

Schema Validation at Publish Time

The Hermodr.Publisher.EventSchema package adds a middleware component that automatically validates every event's payload against its registered schema before the event reaches any publish channel.

Prerequisites

dotnet add package Hermodr.Publisher.EventSchema

Quick start

Register the schema validation middleware on the publisher builder:

using Hermodr;

builder.Services
.AddEventPublisher(opts => opts.Source = new Uri("https://example.com"))
.UseSchemaValidation()
.AddChannel<RabbitMqPublishChannel>();

UseSchemaValidation() auto-registers all schema services (IEventSchemaRegistry, IEventSchemaValidator, deserializers) if they are not already registered.

Registering schemas

Schemas must be registered before validation can run. Pass them via the configureSchemaServices callback:

builder.Services
.AddEventPublisher()
.UseSchemaValidation(
configureOptions: null,
configureSchemaServices: schema =>
{
schema.SchemaRegistrations.Add(registry =>
{
var schema = new EventSchema("order.placed", "1.0", "application/json");
schema.Properties.Add(new EventProperty("orderId", "string", "1.0"));
schema.Properties[0]!.Constraints.Add(new PropertyRequiredConstraint());
schema.Properties.Add(new EventProperty("amount", "decimal", "1.0"));
schema.Properties[1]!.Constraints.Add(new PropertyRequiredConstraint());
registry.Register(schema);
});
});

You can also register schemas from CLR types using assembly scanning:

schema.AssembliesToScan.Add(typeof(OrderPlacedData).Assembly);

Configuration

Missing schema behavior

Controls what happens when no schema is registered for an event type:

BehaviorDescription
Allow (default)The event is published without validation
BlockA SchemaValidationException is thrown
FallbackA schema with no constraints is used (all properties pass)
.UseSchemaValidation(opts =>
{
opts.MissingSchemaBehavior = MissingSchemaBehavior.Block;
})

Validation failure behavior

Controls what happens when the payload does not match the schema:

BehaviorDescription
Throw (default)A SchemaValidationException is thrown with all validation errors
LogErrors are logged but the event is still published
.UseSchemaValidation(opts =>
{
opts.ValidationFailureBehavior = ValidationFailureBehavior.Log;
})

Version-aware validation

The middleware supports schema versioning through the dataversion CloudEvent extension attribute. When present, the exact version is looked up; when absent, the latest registered version is used.

var @event = new CloudEvent
{
Type = "order.placed",
Source = new Uri("https://example.com"),
Data = data
};

// Set the version extension attribute
var attr = CloudEventAttribute.CreateExtension("dataversion", CloudEventAttributeType.String);
@event[attr] = "2.0";

How it works

The middleware runs after enrichment and before channel dispatch:

  1. Looks up the schema by CloudEvent.Type (and optional dataversion extension)
  2. Deserializes the event payload using a format-agnostic deserializer (JSON, XML, etc.)
  3. Validates every property against the schema's constraints (required, range, enum)
  4. On failure: throws or logs according to ValidationFailureBehavior
  5. On success: calls next(context) to continue the pipeline