Skip to content

Cratis Chronicle event compensation in .NET

Cratis Chronicle event compensation appends a new event that offsets an earlier event without changing it. Use compensation when the correction must remain part of the record, such as a reversal of a posted ledger entry. Your observers decide how that correction affects application state.

Mark the correcting event type with CompensationFor<TEventType> alongside EventType. In this example, EntryReversed can compensate an EntryPosted event:

[EventType]
public record EntryPosted(decimal Amount);
[EventType, CompensationFor<EntryPosted>]
public record EntryReversed(string Reason);

Chronicle registers the link with the event type’s schema, not as a content property on each event. A compensation event type can compensate for one event type. Reason describes this reversal; you do not add the original event’s sequence number to the record type.

Call Compensate on the sequence that holds the original event. Pass that event’s sequence number and the new correction:

var result = await eventLog.Compensate(sequenceNumber, new EntryReversed("Posted to the wrong account"));

Check result.IsSuccess before treating the correction as recorded. Compensate returns an AppendResult, like Append; the attribute alone does not make the append succeed.

You do not pass an event source, event stream, or subject because the new event inherits those from the original. A rejected compensation reports AppendError.CompensatedEventNotFound if the original does not exist, or AppendError.EventTypeDoesNotCompensateEventType if the correcting type does not compensate the original’s type. Inspect result.Errors for these errors. Normal append constraints and compliance still apply.

Observers receive the correcting event like any other append. Read EventContext.CompensationFor to find the original event’s sequence number:

if (context.CompensationFor is { } compensated)
{
// This event compensates for the event at `compensated`.
}

CompensationFor is null for events that are not compensations. Recording a correction does not itself reverse an external action. For how observers interpret compensations and how to create one in the Workbench, see Event compensation.

Event sequence implementations that do not support compensations, such as the no-op event log used in tests, throw CompensationsNotSupported from Compensate.