Skip to content

CHR0015: Projection must not have side effects

A class implementing IProjectionFor<T> must not inject ICommandPipeline or IEventLog via its constructor. Projections are pure state builders that derive read models from events and must not produce side effects.

Error

using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;
using Cratis.Chronicle.Projections;
[EventType]
public record Chr0015UserRegistered;
public class Chr0015UserReadModel;
public class Chr0015UserProjection : IProjectionFor<Chr0015UserReadModel>
{
readonly IEventLog _eventLog;
// CHR0015: Projections must not take dependencies through the constructor
public Chr0015UserProjection(IEventLog eventLog)
{
_eventLog = eventLog;
}
public void Define(IProjectionBuilderFor<Chr0015UserReadModel> builder) =>
builder.From<Chr0015UserRegistered>();
}
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record Chr0015UserRegisteredFixed;
public class Chr0015UserReadModelFixed;
public class Chr0015UserProjectionFixed : IProjectionFor<Chr0015UserReadModelFixed>
{
// Now valid - no constructor dependencies
public void Define(IProjectionBuilderFor<Chr0015UserReadModelFixed> builder) =>
builder.From<Chr0015UserRegisteredFixed>();
}

Projections declare how to build a read model from a stream of events. They are evaluated at replay time and must be purely declarative. Injecting IEventLog or ICommandPipeline implies intent to perform side effects during projection evaluation, which breaks idempotency and replay safety.

  • CHR0016: Projection Define() must not contain imperative code
  • CHR0017: Constraint must not have side effects