Optima.Net.Events
1.0.7
Prefix Reserved
dotnet add package Optima.Net.Events --version 1.0.7
NuGet\Install-Package Optima.Net.Events -Version 1.0.7
<PackageReference Include="Optima.Net.Events" Version="1.0.7" />
<PackageVersion Include="Optima.Net.Events" Version="1.0.7" />
<PackageReference Include="Optima.Net.Events" />
paket add Optima.Net.Events --version 1.0.7
#r "nuget: Optima.Net.Events, 1.0.7"
#:package Optima.Net.Events@1.0.7
#addin nuget:?package=Optima.Net.Events&version=1.0.7
#tool nuget:?package=Optima.Net.Events&version=1.0.7
Optima.Net.Events
A schema-driven event serialization framework for .NET supporting Avro and Protobuf.
Optima.Net.Events provides deterministic schema generation, schema-hash validation, and payload normalization to ensure safe event exchange between distributed systems.
If you like what I built here or it helped you learn something new,
please consider buying me a coffee:
https://buymeacoffee.com/snamretsuek
Your support is greatly appreciated!
Overview
Optima.Net.Events enables schema-safe event exchange between microservices using either Avro or Protobuf.
The library focuses on:
- deterministic schema generation
- schema hash validation
- payload normalization
- transport independence
- compatibility across service versions
This ensures events remain forward and backward compatible across services.
Architecture Overview
The library is intentionally structured around a small set of clear responsibilities.
IPayload
?
Payload (base implementation)
?
DynamicPayload (runtime payload convenience)
?
GenericEvent<TPayload> (event envelope)
?
Schema Generators
??? AvroSchemaGenerator
??? ProtobufSchemaGenerator
?
Serializers
??? AvroEventSerializer
??? ProtobufEventSerializer
Infrastructure depends only on the IPayload contract, not specific implementations.
Event Envelope
All events are wrapped in a single generic envelope.
GenericEvent<TPayload>
The envelope contains:
- EventId
- EventType
- Source
- Timestamp
- SchemaVersion
- SchemaHash
- CausationEventId
- CorrelationId
- DeDuplicationId
- StreamId
- Payload
This envelope is transport agnostic and can be used with:
- Kafka
- RabbitMQ
- Azure Service Bus
- EventStoreDB
- HTTP event streaming
Payload Model
Payloads implement the contract:
public interface IPayload
{
string PayloadName { get; }
IReadOnlyDictionary<string, object> Fields { get; }
}
The library provides a base implementation:
public abstract class Payload : IPayload
The base class guarantees:
- consistent field storage
- deterministic schema generation
- normalized field names
- fluent payload construction
Runtime Payload Example
var payload = new DynamicPayload("PaymentRequested")
.Add("SourceAccountNumber", "123456")
.Add("TargetAccountNumber", "987654")
.Add("Amount", 1500.75)
.Add("Currency", "USD");
DynamicPayload is a convenience class that allows runtime payload
creation.
Declared Payload Example
public class PaymentRequestedPayload : Payload
{
public PaymentRequestedPayload(decimal amount, string currency)
: base(nameof(PaymentRequestedPayload))
{
Add("Amount", amount);
Add("Currency", currency);
}
}
Declared payloads provide stronger domain modelling and clearer contracts.
Avro Serialization Example
var payload = new DynamicPayload("PaymentRequested")
.Add("Amount", 1500.75)
.Add("Currency", "USD");
var evt = new GenericEvent<DynamicPayload>
{
Source = "PaymentsService",
SchemaVersion = "V1.0.0",
Payload = payload
};
var schema = AvroSchemaGenerator.GenerateSchemaFromGenericEvent(evt);
var bytes = AvroEventSerializer.Serialize(evt, schema);
var result = AvroEventSerializer.Deserialize(bytes, schema);
Protobuf Serialization Example
var payload = new DynamicPayload("PaymentRequested")
.Add("Amount", 1500.75)
.Add("Currency", "USD");
var evt = new GenericEvent<DynamicPayload>
{
Source = "PaymentsService",
SchemaVersion = "V1.0.0",
Payload = payload
};
var schema = ProtobufSchemaGenerator.GenerateSchemaProto(
payload.Fields,
payload.PayloadName
);
var bytes = ProtobufEventSerializer.Serialize(evt, schema);
var result = ProtobufEventSerializer.Deserialize(bytes, schema);
Deterministic Schema Generation
Both schema generators ensure stable ordering of fields.
This guarantees:
- identical payloads always produce identical schemas
- schema hashes remain stable
- distributed systems cannot drift accidentally
This is critical for schema-hash validation.
Schema Hash Validation
During serialization a SHA256 schema hash is computed.
During deserialization the hash is validated to ensure:
- the correct schema was used
- schemas have not changed
- event integrity is preserved
Event Lifecycle
Typical lifecycle of an event using this library:
Domain creates payload
?
Payload wrapped in GenericEvent
?
Schema generated
?
Event serialized
?
Event transported
?
Event received
?
Schema hash validated
?
Payload reconstructed
Automatic Payload Discovery
Payloads can be automatically discovered by scanning domain assemblies.
Example DI setup:
services.AddEventPayloadDiscovery(
typeof(SomeDomainPayload).Assembly
);
Discovery enables tooling such as:
- automatic schema generation
- event catalogs
- compatibility checks
- documentation generation
CLI Schema Generation
It is recommended to implement a small CLI tool that scans assemblies and generates schemas.
Example:
dotnet run -- generate-schemas --assembly MyDomain.dll
This allows teams to:
- version schemas
- publish schemas to a registry
- validate schema evolution in CI pipelines
Recommended Project Structure
Example Clean Architecture layout:
MySystem.Domain
Payloads
Aggregates
Domain Events
MySystem.Application
DI setup
Event publishing
MySystem.Infrastructure
Optima.Net.Events integration
Schema management
Payload discovery should be triggered in the Application layer by scanning the Domain assembly.
Comparison
Feature Avro Protobuf
Schema format JSON (.avsc) Text (.proto) Runtime flexibility High Medium Performance Fast Very fast Schema evolution Native support Manual numbering Payload model Dynamic or declared Dynamic dictionary
Performance depends heavily on payload structure and runtime environment.
Always run your own benchmarks.
Optional Envelope Pattern
Optima.Net.Events does not enforce a transport envelope.
However many systems wrap events in something like:
public sealed record EventEnvelope<TEvent>(
Guid EventId,
DateTimeOffset Timestamp,
string SchemaHash,
TEvent Payload);
This allows:
- retrieving schemas via SchemaHash
- validating events before deserialization
- consistent event metadata recording
Compatibility
- .NET 8.0 or later
- Windows
- Linux
- macOS
License
MIT License � 2025
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Apache.Avro (>= 1.12.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.3)
- Optima.Net (>= 1.0.9)
- protobuf-net (>= 3.2.56)
- protobuf-net.Reflection (>= 3.2.52)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Optima.Net.Events:
| Package | Downloads |
|---|---|
|
Optima.Net.DomainModel
Optima.Net.DomainModel Defines the immutable structural foundation of the domain, enforcing invariants and preventing illegal domain states without handling behavior, workflows, or persistence. |
GitHub repositories
This package is not used by any popular GitHub repositories.
RELEASENOTES.md