Skip to content

Counters

Counters track how many times events occur or increment/decrement numeric values. Chronicle provides three counter operations: count, increment, and decrement.

The count operation increments a property by 1 each time an event occurs:

count {Property}
from PageViewed
count ViewCount
LastViewedAt = $eventContext.occurred

Each PageViewed event increases ViewCount by 1.

The increment operation increases a property by 1:

increment {Property}
from UserLoggedIn
increment LoginCount
LastLogin = $eventContext.occurred

The decrement operation decreases a property by 1:

decrement {Property}
from ItemConsumed
decrement RemainingStock
LastConsumed = $eventContext.occurred

While count and increment both add 1, they have semantic differences:

  • count: Counts occurrences of a specific event (conceptually counting events)
  • increment: Increases a value (conceptually modifying state)

In practice, they behave identically but express different intent.

You can use multiple counter operations in the same event:

from OrderPlaced
increment TotalOrders
count OrderCount
add TotalRevenue by total
projection User => UserReadModel
from UserRegistered
Name = name
Email = email
from UserLoggedIn
count LoginCount
LastLogin = $eventContext.occurred
from UserProfileViewed
increment ProfileViewCount
projection Product => ProductReadModel
from ProductCreated
Name = name
StockLevel = initialStock
from ItemAdded
increment StockLevel
from ItemSold
decrement StockLevel
count SalesCount
from ItemReturned
increment StockLevel
decrement SalesCount
projection BlogPost => BlogPostReadModel
from PostPublished
Title = title
Content = content
from PostViewed
count ViewCount
from PostLiked
increment LikeCount
from PostUnliked
decrement LikeCount
from CommentAdded
count CommentCount

Account Balance (Alternative to Arithmetic)

Section titled “Account Balance (Alternative to Arithmetic)”
projection Account => AccountReadModel
from AccountOpened
AccountNumber = accountNumber
Balance = initialBalance
from DepositMade
increment TransactionCount
add Balance by amount
from WithdrawalMade
increment TransactionCount
subtract Balance by amount
projection ForumUser => ForumUserReadModel
from UserJoined
Username = username
from QuestionPosted
count QuestionsPosted
from AnswerPosted
count AnswersPosted
from UpvoteReceived
increment Reputation
from DownvoteReceived
decrement Reputation
projection Project => ProjectReadModel
from ProjectCreated
Name = name
from TaskAdded
increment TotalTasks
from TaskCompleted
increment CompletedTasks
decrement RemainingTasks
from TaskAdded
increment RemainingTasks

The target property must be a numeric type:

  • int
  • long
  • decimal
  • double
  • float

Attempting to use counters on non-numeric properties will result in a validation error.

Counter properties should have an initial value:

public class UserReadModel
{
public string Name { get; set; }
public int LoginCount { get; set; } = 0; // Initialize to 0
public int ProfileViews { get; set; } = 0;
}
  1. Initialize to Zero: Ensure counter properties start at 0 in the read model
  2. Use Count for Events: Use count when tracking event occurrences
  3. Use Increment/Decrement for State: Use increment/decrement for state changes
  4. Combine with Timestamps: Pair counters with timestamps for richer tracking
  5. Validate Decrements: Ensure decrements won’t result in negative values if that’s not valid for your domain
  6. Consider Arithmetic: For more complex numeric operations, use Arithmetic operations