From All
FromAll subscribes to every event type in the event sequence, including types not declared by the projection. It can create or update read-model instances for any event source, keyed by the event source id unless another key is configured. The declaration language’s all block has the same scope. If you only want the projection’s own handled event types, use From Every Event instead.
Fluent .NET Projection
Section titled “Fluent .NET Projection”The fluent .FromAll(...) API maps read model properties for every event in the sequence. Use event context values for mappings that need to work across unrelated event payloads.
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';import { field } from '@cratis/fundamentals';
@eventType()class InventoryRegisteredFromAll { @field(String) readonly productName: string;
constructor(productName: string) { this.productName = productName; }}
@eventType()class InventoryAdjustedFromAll { @field(Number) readonly quantity: number;
constructor(quantity: number) { this.quantity = quantity; }}
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) .fromAll(_ => _ .set(m => m.lastUpdated) .toEventContextProperty('occurred')); }}Combining fluent FromAll and FromEvery applies both sets of mappings to the projection’s handled event types; FromAll also covers otherwise-unhandled event types.
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]end@fromAllEvents(property?, contextProperty?) subscribes the root projection to
every event type, like fluent fromAll() and .NET [FromAll]. Omit both arguments
to map the property with the same name on the event, or supply a source property
name. The optional second argument selects an event context property instead.
Both legacy and standard TypeScript decorators are supported.
Its mappings also apply to child projections through the root’s All definition
with IncludeChildren; they can update or clear child properties as events are
processed. Child and nested @fromAllEvents members contribute to that same
root block by bare property name (for example, title, not items.title).
Decorating only a child or nested model sets IncludeChildren for the shared
block, including the root’s restricted shared mappings, but does not widen the
root subscription. This flag does not guarantee child updates: with kernel
19.26.2, when the child’s creating event is explicitly subscribed at the root,
the event updates the root’s shared mappings and creates the child without
populating its shared mapped values. Opt into @fromAllEvents at the root to
populate child values in this case.
The compiler has one shared All block per read model. When any root property
opts into @fromAllEvents, every root @fromEvery/@fromAll mapping (including
inherited ones) also becomes an all-event mapping and applies to children.
@fromEvery/@fromAll mappings on child or nested types are not collected,
unlike .NET, which also collects child [FromEvery] mappings. Restricted and
all-event shared mappings cannot be mixed in one model: an unrelated event can
clear an existing sibling mapping too.
If @fromAllEvents and @fromEvery (or @fromAll) target the same property,
@fromAllEvents takes precedence regardless of decorator order.
Opting into all-event subscriptions
Section titled “Opting into all-event subscriptions”Changing a mapping to @fromAllEvents changes the projection definition and may
trigger automatic replay. Upgrading alone does not change existing @fromAll
definitions. With all-event subscriptions, unrelated events can create a read
model per event source or clear mapped properties when their payload lacks a
value. Removal events can still delete a read model. These effects also matter
for child mappings.
Other evolution policies may require you to initiate replay; @notRewindable
projections only apply the new behavior to future events after you opt in.
Use @fromEvery without @fromAllEvents on the model or its children when you
want to map only events already subscribed to, without applying these shared
mappings to children.
@fromAll remains a supported, deprecated alias for restricted mappings; prefer
@fromEvery. Contrary to the earlier ‘next major’ announcement, its behavior will
not change; use @fromAllEvents to opt into all-event subscriptions.
@fromEvery still takes precedence over @fromAll on the same property.
import { eventType, fromAllEvents, fromEvent } from '@cratis/chronicle';import { field } from '@cratis/fundamentals';
@eventType()export class ProductRenamedFromAllConvention { @field(String) readonly name: string; @field(Number) readonly version: number;
constructor(name: string, version: number) { this.name = name; this.version = version; }}
@eventType()export class ProductPriceChangedFromAllConvention { @field(Number) readonly price: number; @field(Number) readonly version: number;
constructor(price: number, version: number) { this.price = price; this.version = version; }}
@fromEvent(ProductRenamedFromAllConvention)@fromEvent(ProductPriceChangedFromAllConvention)export class ProductVersionFromAllConvention { @field(String) name = ''; @field(Number) price = 0;
@fromAllEvents() 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.
Child and nested properties
Section titled “Child and nested properties”In the .NET model-bound API, [FromAll] on a child or nested object’s property maps into that object, including on the event that creates it. For example, a child LastValue mapped from the event’s Value receives that value on its ChildrenFrom event. It does not write a root LastValue, even if the root declares a property with the same name.
A root mapping such as Source from the event source id stays on the root; it is not copied into children. Child-level attributes do not enable sequence-wide subscription on the root. Use a root [FromAll] when the root must subscribe to all events.
Older .NET clients, and the TypeScript client until it emits child-level mappings, send child mappings as bare root property names. The kernel moves such a mapping to each matching descendant only when the root schema does not declare that property. If both root and child declare it, the mapping stays at the root until you upgrade the client. Replay affected projections to rebuild previously materialized child values.
Choosing Between FromAll And FromEvery
Section titled “Choosing Between FromAll And FromEvery”| API | Use it when |
|---|---|
.FromAll(...) | You want a fluent mapping that subscribes to every event type in the sequence. |
[FromAll] | You want sequence-wide subscription with convention-based payload mapping in a .NET model-bound projection. |
[FromEvery(...)] | You want to map only handled event types, with an explicit event payload or event context source. |
| 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”- A join-only event type is excluded from the separate all-events subscription to avoid double counting. Its
FromAllmapping follows the join, so an event with no matching read-model instance does not create a fallback instance under its own event source id. To construct an instance for that type, declare it inFromas well; it then receives the every-event mapping once throughFrom. FromAllis a .NET API name; the declaration language calls its sequence-wide subscriptionall.- 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.
- Counting or incrementing into a dictionary-typed property with a dynamic, per-event key is available only through the fluent
.FromAll(...)API today, not through[FromAll]. See Projection With FromAll.