Skip to content

Arithmetic Operations

Arithmetic operations allow you to add or subtract values from numeric properties using values from events. Unlike counters that always change by 1, arithmetic operations use variable amounts.

The add operation increases a property by a specified amount:

add {Property} by {expression}
from PaymentReceived
add Balance by amount
LastPayment = $eventContext.occurred

The subtract operation decreases a property by a specified amount:

subtract {Property} by {expression}
from WithdrawalMade
subtract Balance by amount
LastWithdrawal = $eventContext.occurred

The expression after by can be:

  • Event property: amount, value, cost.total
  • Literal: 100, 10.50
  • Template: `${baseAmount}`

Combine multiple operations in one event:

from OrderPlaced
add TotalRevenue by total
add OrderCount by 1
increment TotalOrders
projection Account => AccountReadModel
from AccountOpened
AccountNumber = accountNumber
Balance = initialBalance
from DepositMade
add Balance by amount
LastDeposit = $eventContext.occurred
from WithdrawalMade
subtract Balance by amount
LastWithdrawal = $eventContext.occurred
from InterestApplied
add Balance by interestAmount
projection Customer => CustomerReadModel
from CustomerRegistered
Name = name
Points = 0
from PurchaseMade
add Points by pointsEarned
from PointsRedeemed
subtract Points by pointsUsed
LastRedemption = $eventContext.occurred
projection Product => ProductReadModel
from ProductCreated
Name = name
StockLevel = initialStock
from StockAdded
add StockLevel by quantity
LastRestocked = $eventContext.occurred
from StockSold
subtract StockLevel by quantity
from StockAdjusted
add StockLevel by adjustmentAmount
projection Budget => BudgetReadModel
from BudgetCreated
Category = category
AllocatedAmount = amount
RemainingAmount = amount
from ExpenseRecorded
subtract RemainingAmount by cost
add TotalSpent by cost
from BudgetIncreased
add AllocatedAmount by additionalAmount
add RemainingAmount by additionalAmount
projection Order => OrderReadModel
from OrderPlaced
OrderNumber = orderNumber
Subtotal = 0
Tax = 0
Total = 0
from LineItemAdded
add Subtotal by itemTotal
add Total by itemTotal
from TaxCalculated
add Tax by taxAmount
add Total by taxAmount
from DiscountApplied
subtract Subtotal by discountAmount
subtract Total by discountAmount
projection PlayerScore => PlayerScoreReadModel
from PlayerJoined
PlayerName = name
Score = 0
from PointsEarned
add Score by points
LastScored = $eventContext.occurred
from PointsLost
subtract Score by points
from BonusAwarded
add Score by bonusPoints
add BonusTotal by bonusPoints

You can use nested properties from events:

from TransactionProcessed
add Balance by transaction.amount
add TotalTransactions by 1

Combine arithmetic with counters for comprehensive tracking:

from SaleCompleted
add TotalRevenue by saleAmount
add TaxCollected by taxAmount
increment SalesCount
count CompletedTransactions

The target property must be a numeric type:

  • int
  • long
  • decimal
  • double
  • float

The expression value must be compatible with the target type.

Chronicle handles arithmetic operations safely:

  • Type mismatches result in compilation errors
  • Ensure the event property used exists and is numeric
  • Consider domain rules (e.g., balance can’t go negative) in your application logic
  1. Initialize Values: Ensure numeric properties have initial values
  2. Use Appropriate Types: Use decimal for money, int for counts
  3. Track Both Directions: Pair add with corresponding subtract for bidirectional operations
  4. Audit Trail: Combine with timestamps and counters for complete tracking
  5. Validation: Consider adding validation in your domain to prevent invalid states
  6. Precision: Be aware of floating-point precision issues with double and float
OperationAmountUse Case
count / increment / decrementAlways ±1Event counting, simple state changes
add / subtractVariable from eventBalances, quantities, scores