Every aggregate instance in Eventuous is stored in its own event stream. The stream name is the key that links an aggregate’s identity to its persisted history. Eventuous follows a predictable default convention and gives you a clean extension point to override it per aggregate type when your storage or projection topology requires a different scheme.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Eventuous/eventuous/llms.txt
Use this file to discover all available pages before exploring further.
StreamName
StreamName is a validated record struct that wraps a plain string. It is used throughout the persistence, subscription, and projection APIs wherever a stream identity is needed.
InvalidStreamName at construction time, preventing silent failures at the persistence layer.
Default naming convention
By default, Eventuous combines the unqualified aggregate type name with the aggregate id value, separated by a hyphen:| Aggregate type | Aggregate id | Stream name |
|---|---|---|
Booking | abc123 | Booking-abc123 |
Payment | pay-9f8e7d | Payment-pay-9f8e7d |
CustomerAccount | cust-42 | CustomerAccount-cust-42 |
-) is important for category subscriptions in stores like KurrentDB/EventStoreDB, where you can subscribe to all events for a given aggregate type.
StreamNameFactory
StreamNameFactory is the static class that builds stream names from type parameters and identity values:
Id suffix from the type name so that BookingId produces Booking-{value} rather than BookingId-{value}:
StreamNameMap
StreamNameMap is an instance-scoped registry that lets you override stream name resolution per identity type. When a mapping is registered for a given TId, it is used instead of the default factory. If no custom mapping exists, the map falls back to StreamNameFactory.
Default stream name resolution
When no custom mapping is registered,GetStreamName delegates to StreamNameFactory.For<TAggregate, TState, TId>, which applies the {AggregateName}-{id} convention:
Custom stream name registration
Register a custom function when you need a different naming scheme for a particular aggregate:Registering a StreamNameMap with DI
Pass a configured StreamNameMap instance to the command service or the aggregate store during DI setup:
How stream names flow through the application
When aCommandService handles a command, it needs to know which stream to load and save the aggregate from/to. The lookup path is:
- The command service calls
StreamNameMap.GetStreamName<TAggregate, TState, TId>(aggregateId). - If a custom mapping for
TIdis registered, the custom function is called. - Otherwise,
StreamNameFactory.For<TAggregate, TState, TId>(aggregateId)produces the default{AggregateName}-{id}name. IEventReader.LoadAggregate/IEventWriter.StoreAggregatereceive the resolvedStreamName.
Helper methods on StreamName
Once you have a StreamName you can extract its parts:
GetCategory() aligns with the KurrentDB/EventStoreDB $ce-{Category} projection that fans out events from all streams in the same category, enabling efficient per-aggregate-type subscriptions.