Passive projection
A passive projection is a projection that is not actively materialized to a persistent store but can be queried on-demand for in-memory lookups. This is useful for scenarios where you need to construct read models from events without the overhead of maintaining persistent state.
Defining a passive projection
Use the .Passive() method to mark a projection as passive:
using Cratis.Chronicle.Projections;
public class UserSummaryProjection : IProjectionFor<UserSummary>
{
public void Define(IProjectionBuilderFor<UserSummary> builder) => builder
.Passive()
.AutoMap()
.From<UserCreated>()
.From<UserUpdated>();
}
This projection:
- Will not be actively maintained in persistent storage
- Can be queried on-demand using the
IProjectionsservice - Reconstructs the read model from events when requested
Using passive projections
Passive projections are accessed through the IProjections interface using the GetInstanceById method:
public class UserService
{
private readonly IProjections _projections;
public UserService(IProjections projections)
{
_projections = projections;
}
public async Task<UserSummary?> GetUserSummaryAsync(string userId)
{
var result = await _projections.GetInstanceById<UserSummary>(userId);
return result.Model;
}
}
Read model definition
The read model is defined the same way as for regular projections:
public record UserSummary(
string Name,
string Email,
int LoginCount,
DateTimeOffset LastLoginAt);
Event definitions
Events should match the read model structure or use explicit mapping:
[EventType]
public record UserCreated(string Name, string Email);
[EventType]
public record UserUpdated(string Name, string Email);
[EventType]
public record UserLoggedIn(DateTimeOffset LoginTime);
How it works
When you call GetInstanceById on a passive projection:
- Chronicle retrieves all relevant events for the specified event source ID
- The projection logic is applied to reconstruct the read model in memory
- The resulting read model is returned without being persisted
- Each call reconstructs the model from scratch, ensuring up-to-date data
When to use passive projections
Passive projections are ideal for:
- Infrequent queries: When read models are accessed rarely or sporadically
- Real-time data: When you always need the most current state without caching concerns
- Memory-sensitive scenarios: When you want to avoid storing projection state
- Temporary calculations: For read models that are computed and discarded
- Testing and debugging: When you need to inspect event-driven state without persistence
Performance considerations
- Passive projections have higher latency since they reconstruct on each request
- They consume more CPU but less storage compared to active projections
- Consider caching strategies if the same passive projection is accessed frequently
- Use for read models with simple event processing logic to minimize reconstruction time