Skip to content

Constant Keys

A constant key fixes the read model key to a literal string value, so all events of a given type accumulate into a single read model instance regardless of which event source they come from.

Use ConstantKey on the [FromEvent] attribute at the class level to route all matching events to a fixed read model instance:

using Cratis.Chronicle.Events;
using Cratis.Chronicle.Projections.ModelBound;
[EventType]
public record MbConstantKeyOrderPlaced(string CustomerName, DateTimeOffset PlacedAt);
[FromEvent<MbConstantKeyOrderPlaced>(ConstantKey = "global")]
public record MbConstantKeyGlobalOrderSummary(
[SetFrom<MbConstantKeyOrderPlaced>(nameof(MbConstantKeyOrderPlaced.CustomerName))]
string LastCustomer,
[SetFrom<MbConstantKeyOrderPlaced>(nameof(MbConstantKeyOrderPlaced.PlacedAt))]
DateTimeOffset LastOrderDate);

Every OrderPlaced event from every event source updates the same GlobalOrderSummary instance.

Count, Increment, and Decrement with ConstantKey

Section titled “Count, Increment, and Decrement with ConstantKey”

Count, Increment, and Decrement attributes also support ConstantKey for collecting events from all event sources into a single document:

using Cratis.Chronicle.Keys;
using Cratis.Chronicle.Projections.ModelBound;
[EventType]
public record MbConstantKeyOrderPlacedForMetrics;
[EventType]
public record MbConstantKeyUserLoggedIn;
[EventType]
public record MbConstantKeyUserLoggedOut;
[EventType]
public record MbConstantKeyErrorOccurred;
public record MbConstantKeySystemMetrics(
[Count<MbConstantKeyOrderPlacedForMetrics>(ConstantKey = "metrics")]
int TotalOrders,
[Increment<MbConstantKeyUserLoggedIn>(ConstantKey = "metrics")]
[Decrement<MbConstantKeyUserLoggedOut>(ConstantKey = "metrics")]
int ActiveSessions,
[Count<MbConstantKeyErrorOccurred>(ConstantKey = "metrics")]
int TotalErrors);

All three properties converge on the "metrics" document regardless of which user or order they come from.

You can mix regular key-based events with constant key events on the same read model:

[EventType]
public record MbConstantKeyUserRegistered;
[EventType]
public record MbConstantKeyOrderPlacedGlobal;
[FromEvent<MbConstantKeyUserRegistered>]
public record MbConstantKeyUserDashboard(
[Key]
Guid UserId,
string Name,
// A per-instance property alongside a constant-keyed one on the same read model
[Count<MbConstantKeyOrderPlacedGlobal>(ConstantKey = "global-stats")]
int PlatformTotalOrders);

Note: When ConstantKey is set on a counter attribute, it affects which read model instance the counter updates — not the key of the read model the attribute belongs to. In this example, PlatformTotalOrders would update the document with key "global-stats", not the UserDashboard instance.

// Events
[EventType]
public record MbConstantKeyProductPurchased(string ProductId, decimal Amount);
[EventType]
public record MbConstantKeyProductReturned(string ProductId, decimal Amount);
[EventType]
public record MbConstantKeyPageViewed(string PageUrl);
// Global read model
public record MbConstantKeyStoreMetrics(
[Count<MbConstantKeyProductPurchased>(ConstantKey = "store")]
int TotalPurchases,
[Count<MbConstantKeyProductReturned>(ConstantKey = "store")]
int TotalReturns,
[Increment<MbConstantKeyProductPurchased>(ConstantKey = "store")]
[Decrement<MbConstantKeyProductReturned>(ConstantKey = "store")]
int NetTransactions,
[Count<MbConstantKeyPageViewed>(ConstantKey = "store")]
int TotalPageViews);

All events from all users and products accumulate into the single StoreMetrics document with key "store".