In-memory store
An @InMemoryProjection keeps its read models on the heap of each instance.
It suits small read models that are cheap to rebuild, and tests.
Declaring
The annotation takes the projection’s name, which is unique ignoring case and at most 255 characters.
The projection is a singleton bean.
class AccountSummary(
@ReadModelKey var accountId: AccountId = AccountId(),
var owner: String = "",
var balance: BigDecimal = BigDecimal.ZERO,
)
@InMemoryProjection(name = "account-summary")
class AccountSummaryProjection {
@InitReadModel
fun init(id: AccountId) = AccountSummary(accountId = id)
@ProjectionListener
fun on(event: MoneyDeposited, summary: AccountSummary) {
summary.balance += event.amount.value
}
}
interface AccountSummaryRepository : InMemoryProjectionRepository<AccountSummary, AccountId> {
fun findByOwner(owner: String): List<AccountSummary>
}
The framework implements the repository, so it needs no @Bean.
It offers derived finders on any property of the read model, is read-only, and returns copies.
Repositories are found in the application’s packages, or only in the packages @EnableProjectionRepositories names.
The repository’s id type is the type of the read model’s own id; see The read model’s own id.
To serve the read models over HTTP, see Read models over HTTP.
Instances and rebuilds
An in-memory projection runs on every instance, each with its own read models.
It is rebuilt on every start.
POST /actuator/consumers/projection/account-summary/rebuild rebuilds it on the instance you call only; see Rebuild and schema evolution.
Errors
An in-memory projection always halts on a message it cannot apply: its error handler is HaltErrorHandler, and naming another for it fails the startup.
See Errors & halts.
Bounding the read models
The number of read models is unbounded by default. Bound it for every in-memory projection, or for one:
spring:
ddd:
cqrs:
projection:
in-memory:
max-read-models: 100000 # every in-memory projection and materialization; 0, the default, sets no bound
projections:
account-summary:
in-memory:
max-read-models: 500000 # this projection only
Only a new key counts against the bound. A change that would exceed it fails the apply, and nothing is evicted. Deleting a read model frees a place.
Restrictions
-
An in-memory projection cannot be another projection’s upstream; the application does not start if one is named as such.
-
The listener runs outside any transaction: the store’s writes cannot roll back with one, so a save or delete inside an active transaction fails the apply.
-
Every read and write copies the read model through JSON, so it must survive a round trip; see Read models are JSON.