Skip to content

Constant Keys

A constant key allows all events of a given type to accumulate into a single read model instance, regardless of which event source the events originate from. This is useful for global aggregates, system-wide counters, and singleton read models.

Use UsingConstantKey(string value) to specify a fixed key value for a from block. The read model is a normal record or class — the constant key becomes its _id in the underlying store:

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record DecConstantKeyOrderPlaced(decimal Total);
public record DecConstantKeyGlobalCounter(int TotalOrders);
public class DecConstantKeyGlobalCounterProjection : IProjectionFor<DecConstantKeyGlobalCounter>
{
public void Define(IProjectionBuilderFor<DecConstantKeyGlobalCounter> builder) => builder
.From<DecConstantKeyOrderPlaced>(_ => _
.UsingConstantKey("global")
.Count(m => m.TotalOrders));
}

Every OrderPlaced event, from every event source, updates the single GlobalCounter instance with the key "global".

TypeScript note: the fluent builder’s count() is not implemented yet (FromBuilder.ts throws at runtime even though it type-checks). This particular example — an unconditional occurrence counter — is C#-only for now. increment()/decrement() are implemented; see the next example.

Constant keys work with all counting and arithmetic functions, making them ideal for global aggregates. The TypeScript example below tracks only activeSessions via increment()/decrement() — it omits the C# example’s count()-based totalUsers, since count() isn’t implemented in TypeScript yet:

[EventType]
public record DecConstantKeyUserRegistered(string Name);
[EventType]
public record DecConstantKeyUserLoggedIn;
[EventType]
public record DecConstantKeyUserLoggedOut;
public record DecConstantKeySiteStatistics(
int TotalUsers,
int ActiveSessions);
public class DecConstantKeySiteStatisticsProjection : IProjectionFor<DecConstantKeySiteStatistics>
{
public void Define(IProjectionBuilderFor<DecConstantKeySiteStatistics> builder) => builder
.From<DecConstantKeyUserRegistered>(_ => _
.UsingConstantKey("site")
.Count(m => m.TotalUsers))
.From<DecConstantKeyUserLoggedIn>(_ => _
.UsingConstantKey("site")
.Increment(m => m.ActiveSessions))
.From<DecConstantKeyUserLoggedOut>(_ => _
.UsingConstantKey("site")
.Decrement(m => m.ActiveSessions));
}

Use UsingConstantParentKey(string value) when working with child collections and you want all events to target the same parent read model:

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record DecConstantKeyUserJoined(string UserId, string UserName);
public record DecConstantKeyTeamMember(string UserId, string Name);
public record DecConstantKeyTeam(IEnumerable<DecConstantKeyTeamMember> Members);
public class DecConstantKeyTeamActivityProjection : IProjectionFor<DecConstantKeyTeam>
{
public void Define(IProjectionBuilderFor<DecConstantKeyTeam> builder) => builder
.Children(m => m.Members, children => children
.IdentifiedBy(e => e.UserId)
.From<DecConstantKeyUserJoined>(_ => _
.UsingConstantParentKey("main-team")
.Set(m => m.Name).To(e => e.UserName)));
}
StrategyMethodWhen to use
Event source ID(default)Each event stream is one instance
Event propertyUsingKey(e => e.Property)Property on the event identifies instance
Event contextUsingKeyFromContext(c => c.Property)Event context property identifies instance
CompositeUsingCompositeKey<T>(...)Multiple values together form identity
ConstantUsingConstantKey("value")All events update the same instance
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections;
[EventType]
public record DecConstantKeyPageViewed(string PageUrl);
[EventType]
public record DecConstantKeyButtonClicked(string ButtonId);
[EventType]
public record DecConstantKeyFormSubmitted(string FormId);
public record DecConstantKeyEngagementMetrics(
int PageViews,
int ButtonClicks,
int FormSubmissions);
public class DecConstantKeyEngagementMetricsProjection : IProjectionFor<DecConstantKeyEngagementMetrics>
{
public void Define(IProjectionBuilderFor<DecConstantKeyEngagementMetrics> builder) => builder
.From<DecConstantKeyPageViewed>(_ => _
.UsingConstantKey("metrics")
.Count(m => m.PageViews))
.From<DecConstantKeyButtonClicked>(_ => _
.UsingConstantKey("metrics")
.Count(m => m.ButtonClicks))
.From<DecConstantKeyFormSubmitted>(_ => _
.UsingConstantKey("metrics")
.Count(m => m.FormSubmissions));
}

This projection collects engagement events from all users and event sources into a single EngagementMetrics document with the key "metrics".