Event Source Id
An event source identifier (EventSourceId) is the key that groups related events together within an event sequence. Every event you append must carry an event source identifier — it is how Chronicle knows which instance of an entity the event belongs to.
How the server works
Section titled “How the server works”Internally, Chronicle represents all event source identifiers as plain strings. This keeps the server and storage layer simple and schema-independent: the server does not need to know whether your identifier is a Guid, an integer, or a domain concept. Any type you use on the client side is serialized to its string representation before it reaches the server.
The EventSourceId type
Section titled “The EventSourceId type”The EventSourceId record is the primary type used throughout the Chronicle API. It wraps a string value and provides implicit conversions from both string and Guid, so you can pass either directly wherever an EventSourceId is expected.
using Cratis.Chronicle.Events;
public static class EventSourceIdConversionExamples{ public static void FromGuid() { // From a Guid — common for aggregate-style identifiers EventSourceId id = Guid.NewGuid(); }
public static void FromString() { // From a string — useful for natural keys EventSourceId id = "order-42"; }
public static void Generated() { // Generate a new random identifier EventSourceId id = EventSourceId.New(); }}The Unspecified sentinel value signals the absence of a meaningful identifier and corresponds to an empty string. Use it when an identifier is structurally required but contextually irrelevant.
The generic EventSourceId<T>
Section titled “The generic EventSourceId<T>”The generic EventSourceId<T> type lets you carry a strongly-typed value through the client-side API while remaining wire-compatible with the plain EventSourceId that the server expects.
using Cratis.Chronicle.Events;using Cratis.Chronicle.EventSequences;
// A strongly-typed domain conceptpublic record EventSourceIdCustomerId(Guid Value) : EventSourceId<Guid>(Value){ public static EventSourceIdCustomerId New() => new(Guid.NewGuid());}
[EventType]public record EventSourceIdOrderPlaced(EventSourceIdCustomerId CustomerId, decimal Total);
public class EventSourceIdOrderService(IEventLog eventLog){ public Task PlaceOrder(decimal total) { var customerId = EventSourceIdCustomerId.New();
// The typed identifier converts implicitly to EventSourceId — no manual conversion needed return eventLog.Append(customerId, new EventSourceIdOrderPlaced(customerId, total)); }}Because EventSourceId<T> carries an implicit conversion to EventSourceId, you can pass a typed identifier anywhere the untyped form is expected — including directly to IEventLog.Append — without any manual conversion. The typed form is most useful when you want the identifier to carry its concrete type through multiple layers so that callers always see the domain concept rather than a plain string.
Frontend proxy generation
Section titled “Frontend proxy generation”When you use EventSourceId<T> in a command or query parameter, the Arc ProxyGenerator reads the underlying type T and emits a TypeScript proxy that reflects the concrete type rather than a plain string. This gives you a fully typed frontend experience that matches the domain model on the server.
See Arc.Chronicle.ProxyGeneration for details on how proxy generation works and how to configure it.