From All
FromAll is the .NET client name for a projection-wide every-event mapping. Other clients expose the same Chronicle definition through their every-event APIs, so use From Every Event for the portable, multi-client concept.
Use FromAll in .NET when you want a property mapping to apply across the event types considered by the projection, especially when defining the projection with the fluent API.
Fluent .NET Projection
Section titled “Fluent .NET Projection”The fluent .FromAll(...) API maps read model properties from event context for all events in the projection definition.
using Cratis.Chronicle.Events;using Cratis.Chronicle.Keys;using Cratis.Chronicle.Projections;
[EventType]public record InventoryRegisteredFromAll(string ProductName);
[EventType]public record InventoryAdjustedFromAll(int Quantity);
public record InventoryStatusFromAll( [property: Key] Guid Id, string ProductName, DateTimeOffset LastUpdated);
public class InventoryStatusFromAllProjection : IProjectionFor<InventoryStatusFromAll>{ public void Define(IProjectionBuilderFor<InventoryStatusFromAll> builder) => builder .From<InventoryRegisteredFromAll>() .From<InventoryAdjustedFromAll>() .FromAll(_ => _ .Set(m => m.LastUpdated) .ToEventContextProperty(c => c.Occurred));}import io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.projections.IProjectionBuilderForimport io.cratis.chronicle.projections.IProjectionForimport java.time.Instant
@EventTypedata class InventoryRegisteredFromAll(val productName: String)
@EventTypedata class InventoryAdjustedFromAll(val quantity: Int)
data class InventoryStatusFromAll( val productName: String = "", val lastUpdated: Instant = Instant.EPOCH)
class InventoryStatusFromAllProjection : IProjectionFor<InventoryStatusFromAll> { override fun define(builder: IProjectionBuilderFor<InventoryStatusFromAll>) { builder .from(InventoryRegisteredFromAll::class) .from(InventoryAdjustedFromAll::class) .fromAll { it.set(InventoryStatusFromAll::lastUpdated).toEventContextProperty("occurred") } }}import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.projections.IProjectionBuilderFor;import io.cratis.chronicle.projections.IProjectionFor;
import java.time.Instant;
@EventTyperecord InventoryRegisteredFromAll(String productName) {}
@EventTyperecord InventoryAdjustedFromAll(int quantity) {}
class InventoryStatusFromAll { public String productName = ""; public Instant lastUpdated = Instant.EPOCH;}
class InventoryStatusFromAllProjection implements IProjectionFor<InventoryStatusFromAll> { @Override public void define(IProjectionBuilderFor<InventoryStatusFromAll> builder) { builder .from(InventoryRegisteredFromAll.class) .from(InventoryAdjustedFromAll.class) .fromAll(all -> { all.set("lastUpdated").toEventContextProperty("occurred"); }); }}defmodule MyApp.Events.InventoryRegisteredFromAll do use Chronicle.Events.EventType, id: "inventory-registered-from-all-v1"
defstruct [:product_name]end
defmodule MyApp.Events.InventoryAdjustedFromAll do use Chronicle.Events.EventType, id: "inventory-adjusted-from-all-v1"
defstruct [:quantity]end
defmodule MyApp.ReadModels.InventoryStatusFromAll do use Chronicle.ReadModels.ReadModel
alias MyApp.Events.{InventoryAdjustedFromAll, InventoryRegisteredFromAll}
defstruct [:id, :product_name, :last_updated]
from InventoryRegisteredFromAll, set: [id: :event_source_id, product_name: :product_name]
from InventoryAdjustedFromAll
from_every set: [last_updated: :occurred]endimport { eventType, Guid, IProjectionBuilderFor, IProjectionFor, projection } from '@cratis/chronicle';
@eventType()class InventoryRegisteredFromAll { constructor(readonly productName: string) {}}
@eventType()class InventoryAdjustedFromAll { constructor(readonly quantity: number) {}}
class InventoryStatusFromAll { id: Guid = Guid.empty; productName = ''; lastUpdated = new Date();}
@projection()class InventoryStatusFromAllProjection implements IProjectionFor<InventoryStatusFromAll> { define(builder: IProjectionBuilderFor<InventoryStatusFromAll>): void { builder .from(InventoryRegisteredFromAll) .from(InventoryAdjustedFromAll) .fromEvery(_ => _ .set(m => m.lastUpdated) .toEventContextProperty('occurred')); }}Model-Bound Attribute
Section titled “Model-Bound Attribute”The [FromAll] attribute is also available in the .NET model-bound API. Use the no-argument form when the read model property name matches the event payload property name by convention.
using Cratis.Chronicle.Events;using Cratis.Chronicle.Keys;using Cratis.Chronicle.Projections.ModelBound;
[EventType]public record ProductRenamedFromAllConvention(string Name, int Version);
[EventType]public record ProductPriceChangedFromAllConvention(decimal Price, int Version);
[FromEvent<ProductRenamedFromAllConvention>][FromEvent<ProductPriceChangedFromAllConvention>]public record ProductVersionFromAllConvention{ [Key] public Guid Id { get; init; }
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
[FromAll] public int Version { get; init; }}import io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.projections.FromAllimport io.cratis.chronicle.projections.FromEventimport io.cratis.chronicle.readModels.ReadModel
@EventTypedata class ProductRenamedFromAllConvention(val name: String, val version: Int)
@EventTypedata class ProductPriceChangedFromAllConvention(val price: Double, val version: Int)
@ReadModel@FromEvent(ProductRenamedFromAllConvention::class)@FromEvent(ProductPriceChangedFromAllConvention::class)data class ProductVersionFromAllConvention( val name: String = "", val price: Double = 0.0,
@FromAll val version: Int = 0)import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.projections.FromAll;import io.cratis.chronicle.projections.FromEvent;import io.cratis.chronicle.readModels.ReadModel;
@EventTyperecord ProductRenamedFromAllConvention(String name, int version) {}
@EventTyperecord ProductPriceChangedFromAllConvention(double price, int version) {}
@ReadModel@FromEvent(eventType = ProductRenamedFromAllConvention.class)@FromEvent(eventType = ProductPriceChangedFromAllConvention.class)record ProductVersionFromAllConvention( String name, double price,
@FromAll int version) {}defmodule MyApp.Events.ProductRenamedFromAllConvention do use Chronicle.Events.EventType, id: "product-renamed-from-all-convention-v1"
defstruct [:name, :version]end
defmodule MyApp.Events.ProductPriceChangedFromAllConvention do use Chronicle.Events.EventType, id: "product-price-changed-from-all-convention-v1"
defstruct [:price, :version]end
defmodule MyApp.ReadModels.ProductVersionFromAllConvention do use Chronicle.ReadModels.ReadModel
alias MyApp.Events.{ProductPriceChangedFromAllConvention, ProductRenamedFromAllConvention}
defstruct [:id, :name, :price, :version]
from ProductRenamedFromAllConvention, set: [id: :event_source_id, name: :name]
from ProductPriceChangedFromAllConvention, set: [id: :event_source_id, price: :price]
# `version` is carried by every event in this projection, so it is mapped # once for all of them instead of being repeated in each `from`. from_every set: [version: :version]endimport { eventType, fromAll, fromEvent, readModel } from '@cratis/chronicle';
@eventType()class ProductRenamedFromAllConvention { constructor( readonly name: string, readonly version: number ) {}}
@eventType()class ProductPriceChangedFromAllConvention { constructor( readonly price: number, readonly version: number ) {}}
@readModel()@fromEvent(ProductRenamedFromAllConvention)@fromEvent(ProductPriceChangedFromAllConvention)class ProductVersionFromAllConvention { name = ''; price = 0;
@fromAll() version = 0;}For explicit model-bound event payload or event context mappings, prefer [FromEvery(...)]. It creates the same every-event projection definition while making the source kind explicit. See From Every Event for examples.
Choosing Between FromAll And FromEvery
Section titled “Choosing Between FromAll And FromEvery”| API | Use it when |
|---|---|
.FromAll(...) | You are using the .NET fluent projection API and want to define an all-events mapping in the projection class. |
[FromAll] | You are using .NET model-bound projections and convention-based payload mapping is enough. |
[FromEvery(...)] | You are using .NET model-bound projections and need to name an event payload or event context source explicitly. |
| Other clients’ every-event API | You are writing Kotlin, Elixir, TypeScript, or another client and the client exposes the concept under a different name. |
Limitations
Section titled “Limitations”FromAllis a .NET client API name, not the portable name used by every client.- The model-bound
[FromAll]attribute is best kept to the convention-based form. - Use an event-specific mapping when a property should update only for selected event types.