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("b5e21a5f-1234-4c3e-9b99-aabbccddeeff")]public class UserRegistered // ❌ mutable class{ public string UserId { get; set; } public string Email { get; set; }}using Cratis.Chronicle.Events;
[EventType("b5e21a5f-1234-4c3e-9b99-aabbccddeeff")]public record UserRegistered(string UserId, string Email); // ✅ immutable recordOr with init-only property syntax for events with many properties:
using Cratis.Chronicle.Events;
[EventType("b5e21a5f-1234-4c3e-9b99-aabbccddeeff")]public record UserRegistered{ public string UserId { get; init; } public string Email { get; init; }}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