Event Souring Library that allows storing Events into a Database. Comes with an implementation of a Azure CosmosDb EventStore.
The CosmosDb Implementation uses the following Microsoft libraries:
Microsoft.Azure.CosmosMicrosoft.Extensions.Configuration.AbstractionsMicrosoft.Extensions.Configuration.BinderMicrosoft.Extensions.Logging.AbstractionsMicrosoft.Extensions.Options
and comes with included compatability to the dependency injection of the .NET Core (Web)HostBuilder.
A Sample can be found in the samples directory.
The library brings a couple of already implemented EventStore packages:
- Papst.EventStore.FileSystem
Note: This is for testing purpose only! - Papst.EventStore.EntityFrameworkCore
- Papst.EventStore.AzureCosmos
- Papst.EventStore.MongoDB
- Events must be attributed with the
EventNameattribute. - EventAggregators, implementing the interface
IEventAggregatoror implementing the abstract classEventAggregatorBaseare automatically added to the Dependency Injection.
It is possible to have multiple EventName attributes on just one event. With the attribute IsWriteName it is possible to define which Identifier is used when writing the event.
[EventName(Name = "MyEventV1", IsWriteName = false)]
[EventName(Name = "MyEventV2")]
public class MyEventsourcingEvent
{
}Reading Events named MyEventV1 or MyEventV2 will deserialize them into a MyEventsourcingEvent.
Writing MyEventsourcingEvent to the Event Stream, will serialize them and name them MyEventV2.
Note: IsWriteName is true by default!
Please refer to the documentation in the relevant implementation sources:
The Event Catalog provides a queryable registry of all events associated with a given entity type, including metadata (description, constraints) and a compile-time generated JSON Schema. This is useful for documentation, API discovery, and runtime introspection.
Use the generic EventNameAttribute<TEntity> to associate an event with an entity:
[EventName<User>("UserCreated", Description = "Raised when a new user is created", Constraints = new[] { "Create" })]
public record UserCreatedEvent(string Name, string Email);
[EventName<User>("UserRenamed", Description = "Raised when a user changes their name", Constraints = new[] { "Update" })]
public record UserRenamedEvent(string NewName);Events can also be discovered automatically via aggregator registrations (EventAggregatorBase<TEntity, TEvent> / IEventAggregator<TEntity, TEvent>).
The same event name may exist for different entity types — the catalog tracks them independently per entity.
When the Papst.EventStore.CodeGeneration package is referenced, the source generator automatically emits an AddCodeGeneratedEventCatalog() extension method alongside the existing AddCodeGeneratedEvents():
var services = new ServiceCollection();
services.AddCodeGeneratedEvents();
services.AddCodeGeneratedEventCatalog();Resolve IEventCatalog from DI and use the async API:
var catalog = serviceProvider.GetRequiredService<IEventCatalog>();
// List all events for an entity
IReadOnlyList<EventCatalogEntry> events = await catalog.ListEvents<User>();
// Filter by name and/or constraints
IReadOnlyList<EventCatalogEntry> filtered = await catalog.ListEvents<User>(
name: "UserCreated",
constraints: new[] { "Create" }
);
// Get event details including JSON Schema (global lookup)
EventCatalogEventDetails? details = await catalog.GetEventDetails("UserCreated");
// Get event details scoped to a specific entity (for duplicate event names across entities)
EventCatalogEventDetails? scoped = await catalog.GetEventDetails<User>("UserCreated");A full working sample is available at samples/SampleEventCatalog/.
For an end-to-end ASP.NET Core example using the in-memory event store, stream aggregation, and read-model repositories, see samples/SampleInMemoryAspNetApi/.
Adds a possibility to update a streams MetaData object.
V5.3 introduces new methods on the IAggregatorStreamContext that allows to transfer information from the Aggregator to the next one.
V5.3 Supports only .NET 10.0 and upwards
V5.2 introduces Metadata on the Stream itself. The IEventStream now got its own metadata Property.
Meta Data for the Stream needs to be set during creation, otherwise it will be empty. To Create a stream with Meta Data the IEventStorehas got a new extended CreateAsync method that takes the additional metadata.
Only the Azure Cosmos Implementation offers a new option in the configuration that updates the TenantId based on the last set event.
V5 comes with a new access model to the streams, with paging and a new library structure.
V5 Supports only .NET 8.0 and upwards
It introduces a separation of EventStore and EventStream. The EventStore now only offers the possibility to create or retrieve streams.
- The
IEventStoreinterface no longer has methods to append to the EventStream - The new
IEventStreamneeds an index document, which needs to be added to existing event streams. See Migration Chapter in Cosmos DB Implementation. - The
IEventStreamAggregatorimplementation that uses the code generated events has moved to a own package to allow removing active code from thePapst.EventStorepackage. - The
EventNameAttribute now uses positional parameters, provided by a constructor. - A single EventStream can no longer contain Events for multiple Entities.
- The
IEventStreamAggregatornow usesValueTaskinstead ofTask
- Meta Data Properties are now of type
string?instead ofGuid?to achieve greater compatability.
- V4 only supports .NET 6.0
It introduces the concept of Code Generated registration of events and aggregators by decorating them with the EventName attribute.
It also decouples the auto generated event type descriptor (was basically a description used to revert to a type using Type.GetType()) from the concrete implementation.
This allows to version and migrate events by just adding a different descriptor.
A sample on how to use the Event Descriptors is found under Samples. The Extension Method AddCodeGeneratedEvents() is automatically generated during compilation if the package Papst.EventStore.CodeGeneration is added to the Project.
Migrate v3 events by adding a EventName attribute and add the typename as a name: [Fullename of the type],[assembly name of the type].
For the MyEventSourcingEvent in the Code Generation Sample it would look like this:
SampleCodeGeneratedEvents.MyEventSourcingEvent,SampleCodeGeneratedEvents
V4 removes support for authenticating with shared keys against the cosmos DB. The implementation is still there, but changed and marked as obsolete.
V3 supports mainly .NET 5.0 and registration of events and event aggregators through reflection