Projection functions
Projections support several built-in functions for mathematical operations and counting. These functions allow you to perform calculations directly within projections without needing custom logic.
TypeScript note: of these, only
Increment()/Decrement()are implemented in TypeScript’s fluent builder today.Count(),Add(), andSubtract()type-check but throwError('... is not implemented yet.')at runtime (FromBuilder.ts), so those examples below are C#-only.
Counting events
Section titled “Counting events”Use Count() to increment a counter each time an event is processed:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record DecFunctionsUserLoggedIn(string Username);
[EventType]public record DecFunctionsUserPerformedAction(string Username, string ActionType);
public record DecFunctionsUserActivity( string Username, int LoginCount, int ActionCount);
public class DecFunctionsUserActivityProjection : IProjectionFor<DecFunctionsUserActivity>{ public void Define(IProjectionBuilderFor<DecFunctionsUserActivity> builder) => builder .AutoMap() .From<DecFunctionsUserLoggedIn>(_ => _ .Count(m => m.LoginCount)) .From<DecFunctionsUserPerformedAction>(_ => _ .Count(m => m.ActionCount));}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.DecFunctionsUserLoggedIn do use Chronicle.Events.EventType, id: "dec-functions-user-logged-in"
defstruct [:username]end
defmodule MyApp.Events.DecFunctionsUserPerformedAction do use Chronicle.Events.EventType, id: "dec-functions-user-performed-action"
defstruct [:username, :action_type]end
defmodule MyApp.ReadModels.DecFunctionsUserActivity do use Chronicle.ReadModels.ReadModel
defstruct [:username, login_count: 0, action_count: 0]end
defmodule MyApp.Projections.DecFunctionsUserActivityProjection do use Chronicle.Projections.Projection, model: MyApp.ReadModels.DecFunctionsUserActivity
alias MyApp.Events.{DecFunctionsUserLoggedIn, DecFunctionsUserPerformedAction}
from DecFunctionsUserLoggedIn, count: :login_count
from DecFunctionsUserPerformedAction, count: :action_countendIncrement and decrement
Section titled “Increment and decrement”Use Increment() and Decrement() to add or subtract 1 from a property:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record DecFunctionsItemAdded(string Name);
[EventType]public record DecFunctionsItemRemoved(string Name);
public record DecFunctionsInventory(int Quantity);
public class DecFunctionsInventoryProjection : IProjectionFor<DecFunctionsInventory>{ public void Define(IProjectionBuilderFor<DecFunctionsInventory> builder) => builder .AutoMap() .From<DecFunctionsItemAdded>(_ => _ .Increment(m => m.Quantity)) .From<DecFunctionsItemRemoved>(_ => _ .Decrement(m => m.Quantity));}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.DecFunctionsItemAdded do use Chronicle.Events.EventType, id: "dec-functions-item-added"
defstruct [:name]end
defmodule MyApp.Events.DecFunctionsItemRemoved do use Chronicle.Events.EventType, id: "dec-functions-item-removed"
defstruct [:name]end
defmodule MyApp.ReadModels.DecFunctionsInventory do use Chronicle.ReadModels.ReadModel
defstruct quantity: 0end
defmodule MyApp.Projections.DecFunctionsInventoryProjection do use Chronicle.Projections.Projection, model: MyApp.ReadModels.DecFunctionsInventory
alias MyApp.Events.{DecFunctionsItemAdded, DecFunctionsItemRemoved}
from DecFunctionsItemAdded, add: [quantity: 1]
from DecFunctionsItemRemoved, subtract: [quantity: 1]endimport { eventType, IProjectionBuilderFor, IProjectionFor, projection } from '@cratis/chronicle';
@eventType()class DecFunctionsItemAdded { name = '';}
@eventType()class DecFunctionsItemRemoved { name = '';}
class DecFunctionsInventory { quantity = 0;}
@projection()class DecFunctionsInventoryProjection implements IProjectionFor<DecFunctionsInventory> { define(builder: IProjectionBuilderFor<DecFunctionsInventory>): void { builder .autoMap() .from(DecFunctionsItemAdded, _ => _ .increment(m => m.quantity)) .from(DecFunctionsItemRemoved, _ => _ .decrement(m => m.quantity)); }}These functions always change the value by exactly 1.
Add and subtract with values
Section titled “Add and subtract with values”Use Add() and Subtract() to add or subtract specific values from event properties:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record DecFunctionsAccountOpened(string Number);
[EventType]public record DecFunctionsMoneyDeposited(decimal Amount);
[EventType]public record DecFunctionsMoneyWithdrawn(decimal Amount);
public record DecFunctionsAccount(string Number, decimal Balance);
public class DecFunctionsAccountProjection : IProjectionFor<DecFunctionsAccount>{ public void Define(IProjectionBuilderFor<DecFunctionsAccount> builder) => builder .AutoMap() .From<DecFunctionsAccountOpened>(_ => _ .Set(m => m.Balance).ToValue(0m)) .From<DecFunctionsMoneyDeposited>(_ => _ .Add(m => m.Balance).With(e => e.Amount)) .From<DecFunctionsMoneyWithdrawn>(_ => _ .Subtract(m => m.Balance).With(e => e.Amount));}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.DecFunctionsAccountOpened do use Chronicle.Events.EventType, id: "dec-functions-account-opened"
defstruct [:number]end
defmodule MyApp.Events.DecFunctionsMoneyDeposited do use Chronicle.Events.EventType, id: "dec-functions-money-deposited"
defstruct [:amount]end
defmodule MyApp.Events.DecFunctionsMoneyWithdrawn do use Chronicle.Events.EventType, id: "dec-functions-money-withdrawn"
defstruct [:amount]end
defmodule MyApp.ReadModels.DecFunctionsAccount do use Chronicle.ReadModels.ReadModel
defstruct number: "", balance: 0end
defmodule MyApp.Projections.DecFunctionsAccountProjection do use Chronicle.Projections.Projection, model: MyApp.ReadModels.DecFunctionsAccount
alias MyApp.Events.{DecFunctionsAccountOpened, DecFunctionsMoneyDeposited, DecFunctionsMoneyWithdrawn}
from DecFunctionsAccountOpened, set: [balance: 0]
from DecFunctionsMoneyDeposited, add: [balance: :amount]
from DecFunctionsMoneyWithdrawn, subtract: [balance: :amount]endSupported types
Section titled “Supported types”All projection functions work with these numeric types:
intlongfloatdoubledecimal
The functions automatically handle type conversion and maintain the target property’s type.
How functions work
Section titled “How functions work”- Initialization: Properties start at 0 (or their default value) when first accessed
- Accumulation: Functions apply their operations incrementally as events are processed
- Type safety: Values are converted to match the target property type
- State preservation: Current values are maintained between events
Combining functions
Section titled “Combining functions”You can use multiple functions in a single projection:
[EventType]public record DecFunctionsTransaction(decimal Amount);
public record DecFunctionsTransactionSummary( int TransactionCount, decimal TotalAmount, int ProcessedEvents);
public class DecFunctionsTransactionSummaryProjection : IProjectionFor<DecFunctionsTransactionSummary>{ public void Define(IProjectionBuilderFor<DecFunctionsTransactionSummary> builder) => builder .From<DecFunctionsTransaction>(_ => _ .Count(m => m.TransactionCount) .Add(m => m.TotalAmount).With(e => e.Amount) .Increment(m => m.ProcessedEvents));}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.DecFunctionsTransaction do use Chronicle.Events.EventType, id: "dec-functions-transaction"
defstruct [:amount]end
defmodule MyApp.ReadModels.DecFunctionsTransactionSummary do use Chronicle.ReadModels.ReadModel
defstruct transaction_count: 0, total_amount: 0, processed_events: 0end
defmodule MyApp.Projections.DecFunctionsTransactionSummaryProjection do use Chronicle.Projections.Projection, model: MyApp.ReadModels.DecFunctionsTransactionSummary
alias MyApp.Events.DecFunctionsTransaction
from DecFunctionsTransaction, count: :transaction_count, add: [total_amount: :amount, processed_events: 1]endThese functions provide powerful aggregation capabilities while keeping projection logic simple and declarative.