CHR0021: Event types should be record types
Rule Description
Section titled “Rule Description”Types decorated with [EventType] should be declared as record types rather than classes.
Severity
Section titled “Severity”Warning
Example
Section titled “Example”Violation
Section titled “Violation”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 recordOr 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;}Why This Rule Exists
Section titled “Why This Rule Exists”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
recordsignals to readers that the type is a value/fact, not a mutable entity
Related Rules
Section titled “Related Rules”- CHR0012: Event types should avoid nullable properties