Aggregate identities in Eventuous are strong types, not raw strings. Wrapping every identity in its ownDocumentation 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.
record type turns a category of runtime bugs — accidentally passing a RoomId where a BookingId is expected — into a compile-time error. The Id abstract record provides the validation, conversion, and formatting behaviour shared by all identities in the system.
The Id abstract record
Value
The underlying string value. Id validates that this is not null, empty, or whitespace in the constructor. Any violation throws InvalidIdException immediately, so an Id instance can never represent an empty stream name.
ToString()
Returns Value directly. Because event store stream names are derived from Id.ToString(), the string representation of your identity is the stream key — keep it stable across deployments.
Implicit conversion to string
Id subtype wherever a string is expected without an explicit cast. If id is null, the conversion throws InvalidIdException rather than silently producing a null string.
Deconstruct
Creating a typed identity
Declare a one-linerecord that calls the Id base constructor with the value:
- Null/whitespace validation on construction.
- Value equality via record semantics (
==,!=,.Equals()). - A stable
ToString()and implicitstringcast. - Compiler-enforced type safety — a
BookingIdcannot be accidentally used as aRoomId.
BookingId from the sample application looks exactly like this:
Why typed identities matter
Consider a booking service that works with several aggregate types:Booking, Room, and Guest. Each has its own identity:
InvalidIdException
Thrown by the Id constructor when value is null, empty, or whitespace. Two overloads exist:
Id subtype, making it straightforward to identify which aggregate identity was invalid.
AggregateId is obsolete
Earlier versions of Eventuous shipped an AggregateId base class. It still compiles but carries an [Obsolete] attribute:
AggregateId should be migrated to derive from Id directly. The behaviour is identical.
The
CommandService<TAggregate, TState, TId> generic parameter TId has a where TId : Id constraint. All three type parameters must align — your aggregate, its state record, and its identity must all reference the same TId type.