Skip to content

CHR0021: Event types should be record types

Types decorated with [EventType] should be declared as record types rather than classes.

Warning

using Cratis.Chronicle.Events;
[EventType("f47ac10b-58cc-4372-a567-0e02b2c3d479")]
public class Chr0021UserRegistered // CHR0021: mutable class
{
public string UserId { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
using Cratis.Chronicle.Events;
[EventType("f47ac10b-58cc-4372-a567-0e02b2c3d479")]
public record Chr0021UserRegisteredFixed(string UserId, string Email); // Immutable record

Or with init-only property syntax for events with many properties:

using Cratis.Chronicle.Events;
[EventType("f47ac10b-58cc-4372-a567-0e02b2c3d480")]
public record Chr0021UserRegisteredWithInit
{
public string UserId { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
}

Events in Chronicle are permanent, immutable facts appended to the event log and replayed to rebuild read-model state. Using a class allows the event object to be mutated after creation, which can lead to subtle bugs—for example, modifying an event during a handler before it is fully processed, or producing different results across replays.

Using a record type provides:

  • Immutability by default — positional parameters are init-only
  • Structural equality — two events with the same data are considered equal
  • Concise syntax — positional record syntax keeps event definitions compact and self-documenting
  • Intent clarity — a record signals to readers that the type is a value/fact, not a mutable entity
  • CHR0012: Event types should avoid nullable properties