Skip to content

Projection With FromEvery

Use an every-event mapping in a declarative projection when a read model property should update whenever any event handled by the projection is processed. This is most useful for audit metadata, event source identifiers, and other fields that should stay synchronized with the latest event.

The exact API name depends on the client. .NET and TypeScript expose FromEvery / fromEvery, while Elixir exposes from_every.

Map event context metadata when the read model needs to know when it was last touched.

Declarative FromEvery
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record UserCreatedDeclarativeEvery(string Name, string Email);
[EventType]
public record UserEmailChangedDeclarativeEvery(string Email);
public record UserProfileDeclarativeEvery(
string Name,
string Email,
DateTimeOffset LastUpdated);
public class UserProfileDeclarativeEveryProjection : IProjectionFor<UserProfileDeclarativeEvery>
{
public void Define(IProjectionBuilderFor<UserProfileDeclarativeEvery> builder) => builder
.From<UserCreatedDeclarativeEvery>()
.From<UserEmailChangedDeclarativeEvery>()
.FromEvery(_ => _
.Set(m => m.LastUpdated)
.ToEventContextProperty(c => c.Occurred));
}

Every-event mappings can map more than one context field.

Map multiple context fields
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record AccountTouchedDeclarativeEvery(string Reason);
public record AccountAuditDeclarativeEvery(
DateTimeOffset LastUpdated,
EventSequenceNumber LastEventSequence,
string LastCorrelationId);
public class AccountAuditDeclarativeEveryProjection : IProjectionFor<AccountAuditDeclarativeEvery>
{
public void Define(IProjectionBuilderFor<AccountAuditDeclarativeEvery> builder) => builder
.From<AccountTouchedDeclarativeEvery>()
.FromEvery(_ => _
.Set(m => m.LastUpdated).ToEventContextProperty(c => c.Occurred)
.Set(m => m.LastEventSequence).ToEventContextProperty(c => c.SequenceNumber)
.Set(m => m.LastCorrelationId).ToEventContextProperty(c => c.CorrelationId));
}

Map the event source id when the read model’s primary identifier is not otherwise populated from each event payload.

Map the event source id
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record AccountOpenedDeclarativeEvery(string OwnerName);
public record AccountSummaryDeclarativeEvery(
string AccountId,
string OwnerName);
public class AccountSummaryDeclarativeEveryProjection : IProjectionFor<AccountSummaryDeclarativeEvery>
{
public void Define(IProjectionBuilderFor<AccountSummaryDeclarativeEvery> builder) => builder
.From<AccountOpenedDeclarativeEvery>()
.FromEvery(_ => _
.Set(m => m.AccountId)
.ToEventSourceId());
}

Some clients let the projection decide whether child projection events should update parent-level every-event fields.

Exclude child projection events
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record OrderCreatedDeclarativeEveryExclude(string OrderNumber);
public record OrderAuditDeclarativeEveryExclude(
string OrderNumber,
DateTimeOffset LastUpdated);
public class OrderAuditDeclarativeEveryExcludeProjection : IProjectionFor<OrderAuditDeclarativeEveryExclude>
{
public void Define(IProjectionBuilderFor<OrderAuditDeclarativeEveryExclude> builder) => builder
.From<OrderCreatedDeclarativeEveryExclude>()
.FromEvery(_ => _
.Set(m => m.LastUpdated)
.ToEventContextProperty(c => c.Occurred)
.ExcludeChildProjections());
}

When child projections are included, parent metadata can update for events that only affect a child item.

Include child projection events
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record OrderCreatedDeclarativeEveryChildren(string OrderNumber);
[EventType]
public record ItemAddedDeclarativeEveryChildren(string OrderId, string ProductId, string ProductName, int Quantity);
[EventType]
public record ItemQuantityChangedDeclarativeEveryChildren(string OrderId, string ProductId, int Quantity);
public record OrderDeclarativeEveryChildren(
string OrderNumber,
DateTimeOffset LastModified,
IEnumerable<OrderItemDeclarativeEveryChildren> Items);
public record OrderItemDeclarativeEveryChildren(
string ProductId,
string ProductName,
int Quantity);
public class OrderDeclarativeEveryChildrenProjection : IProjectionFor<OrderDeclarativeEveryChildren>
{
public void Define(IProjectionBuilderFor<OrderDeclarativeEveryChildren> builder) => builder
.From<OrderCreatedDeclarativeEveryChildren>()
.FromEvery(_ => _
.Set(m => m.LastModified)
.ToEventContextProperty(c => c.Occurred))
.Children(m => m.Items, children => children
.IdentifiedBy(m => m.ProductId)
.From<ItemAddedDeclarativeEveryChildren>(_ => _
.UsingKey(e => e.ProductId)
.UsingParentKey(e => e.OrderId))
.From<ItemQuantityChangedDeclarativeEveryChildren>(_ => _
.UsingKey(e => e.ProductId)
.UsingParentKey(e => e.OrderId)));
}

Clients that support multiple every-event declarations merge the property mappings into one projection definition.

Multiple FromEvery declarations
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record UserChangedDeclarativeEveryMultiple(string Name);
public record UserAuditDeclarativeEveryMultiple(
string Name,
DateTimeOffset LastUpdated,
string ModifiedBy);
public class UserAuditDeclarativeEveryMultipleProjection : IProjectionFor<UserAuditDeclarativeEveryMultiple>
{
public void Define(IProjectionBuilderFor<UserAuditDeclarativeEveryMultiple> builder) => builder
.From<UserChangedDeclarativeEveryMultiple>()
.FromEvery(_ => _
.Set(m => m.LastUpdated)
.ToEventContextProperty(c => c.Occurred))
.FromEvery(_ => _
.Set(m => m.ModifiedBy)
.ToEventContextProperty(c => c.CausedBy));
}
Use caseTypical source
Audit timestampEvent context occurrence time
Event sequencingEvent context sequence number
Correlation trackingEvent context correlation id
Event source identificationEvent source id

Use event-specific mappings when the property should update only for selected event types.