CHR0051: Reducer current read model parameter must be nullable
Rule Description
Section titled “Rule Description”Chronicle passes null as the current read model for the event that brings an instance into existence, so a reducer method cannot promise the parameter is never null. Declare the parameter as nullable and handle the null case as the creation of the instance.
Severity
Section titled “Severity”Warning
Example
Section titled “Example”Violation
Section titled “Violation”using Cratis.Chronicle.Events;using Cratis.Chronicle.Reducers;
public record Chr0051ViolationBookStatus(string Title, bool IsAvailable);
[EventType("book-added")]public record Chr0051ViolationBookAdded(string Title);
public class Chr0051ViolationBookStatusReducer : IReducerFor<Chr0051ViolationBookStatus>{ // Warning CHR0051: Reducer method 'Reduce' declares its current read model parameter // as 'Chr0051ViolationBookStatus' rather than 'Chr0051ViolationBookStatus?' public Chr0051ViolationBookStatus Reduce(Chr0051ViolationBookAdded @event, Chr0051ViolationBookStatus current, EventContext context) => new(@event.Title, true);}using Cratis.Chronicle.Events;using Cratis.Chronicle.Reducers;
public record Chr0051FixBookStatus(string Title, bool IsAvailable);
[EventType("book-added")]public record Chr0051FixBookAdded(string Title);
public class Chr0051FixBookStatusReducer : IReducerFor<Chr0051FixBookStatus>{ // The current read model parameter is nullable, handling creation correctly. public Chr0051FixBookStatus Reduce(Chr0051FixBookAdded @event, Chr0051FixBookStatus? current, EventContext context) => current ?? new(@event.Title, true);}Why This Rule Exists
Section titled “Why This Rule Exists”A reducer’s job is to fold an event and the current read model state into the next state. When an event is the first for a given event source, there is no current state — Chronicle passes null for the current parameter. A reducer method that declares current as non-nullable makes a promise it cannot keep, because the very first event for an instance will pass null.
In a #nullable disable context, the annotation carries no claim at all, so Chronicle keeps dispatching to those methods without complaint. This rule fires only when a method makes an explicit non-nullable annotation — the broken promise.
Declare current as nullable and handle the null case — typically by creating the initial instance — to make the reducer correct for both the first event and every subsequent one.