Projection with FromEventSequence
The FromEventSequence() method allows you to specify which event sequence a projection should source events from. This is useful when you have multiple event sequences in your system and want to create projections that only process events from specific sequences.
Defining a projection with specific event sequence
Section titled “Defining a projection with specific event sequence”Use FromEventSequence() to specify the event sequence to source events from:
using Cratis.Chronicle.Projections;
public class DecFromEventSequenceOrderProjection : IProjectionFor<DecFromEventSequenceOrder>{ public void Define(IProjectionBuilderFor<DecFromEventSequenceOrder> builder) => builder .FromEventSequence("order-management") .AutoMap() .From<DecFromEventSequenceOrderCreated>() .From<DecFromEventSequenceOrderUpdated>() .From<DecFromEventSequenceOrderShipped>();}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.import { IProjectionBuilderFor, IProjectionFor, projection } from '@cratis/chronicle';
@projection()class DecFromEventSequenceOrderProjection implements IProjectionFor<DecFromEventSequenceOrder> { define(builder: IProjectionBuilderFor<DecFromEventSequenceOrder>): void { builder .fromEventSequence('order-management') .autoMap() .from(DecFromEventSequenceOrderCreated) .from(DecFromEventSequenceOrderUpdated) .from(DecFromEventSequenceOrderShipped); }}This projection:
- Only processes events from the “order-management” event sequence
- Ignores events from other sequences like “user-management” or “inventory-management”
- Uses the specified sequence for all event handling
Event sequence identification
Section titled “Event sequence identification”Event sequences can be identified using string names or EventSequenceId:
public static class DecFromEventSequenceEventSequences{ public const string OrderManagement = "order-management";}
public class DecFromEventSequenceOrderProjectionWithConstant : IProjectionFor<DecFromEventSequenceOrder>{ public void Define(IProjectionBuilderFor<DecFromEventSequenceOrder> builder) => builder // Using a constant instead of a raw string keeps the sequence identifier consistent // wherever it is referenced. .FromEventSequence(DecFromEventSequenceEventSequences.OrderManagement) .AutoMap() .From<DecFromEventSequenceOrderCreated>();}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.import { IProjectionBuilderFor, IProjectionFor, projection } from '@cratis/chronicle';
const eventSequences = { orderManagement: 'order-management'};
@projection()class DecFromEventSequenceOrderProjectionWithConstant implements IProjectionFor<DecFromEventSequenceOrder> { define(builder: IProjectionBuilderFor<DecFromEventSequenceOrder>): void { builder // Using a constant instead of a raw string keeps the sequence identifier consistent // wherever it is referenced. .fromEventSequence(eventSequences.orderManagement) .autoMap() .from(DecFromEventSequenceOrderCreated); }}Read model definition
Section titled “Read model definition”The read model remains the same regardless of the event sequence:
public record DecFromEventSequenceOrder( string OrderNumber, string CustomerId, decimal TotalAmount, DecFromEventSequenceOrderStatus Status, DateTimeOffset CreatedAt, DateTimeOffset? ShippedAt);
public enum DecFromEventSequenceOrderStatus{ Created, Processing, Shipped, Delivered, Cancelled}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.enum DecFromEventSequenceOrderStatus { Created = 'Created', Processing = 'Processing', Shipped = 'Shipped', Delivered = 'Delivered', Cancelled = 'Cancelled'}
class DecFromEventSequenceOrder { orderNumber = ''; customerId = ''; totalAmount = 0; status = DecFromEventSequenceOrderStatus.Created; createdAt = new Date(); shippedAt: Date | null = null;}Event definitions
Section titled “Event definitions”Events should be designed to work within the specific sequence context:
using Cratis.Chronicle.Events;
[EventType]public record DecFromEventSequenceOrderCreated( string OrderNumber, string CustomerId, decimal TotalAmount);
[EventType]public record DecFromEventSequenceOrderUpdated( string OrderNumber, decimal NewTotalAmount);
[EventType]public record DecFromEventSequenceOrderShipped( string OrderNumber, DateTimeOffset ShippedAt);Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.import { eventType } from '@cratis/chronicle';
@eventType()class DecFromEventSequenceOrderCreated { orderNumber = ''; customerId = ''; totalAmount = 0;}
@eventType()class DecFromEventSequenceOrderUpdated { orderNumber = ''; newTotalAmount = 0;}
@eventType()class DecFromEventSequenceOrderShipped { orderNumber = ''; shippedAt = new Date();}How it works
Section titled “How it works”When using FromEventSequence():
- The projection subscribes only to the specified event sequence
- Events from other sequences are ignored, even if they match the event types
- The projection processes events in the order they appear in the specified sequence
- Event sequence numbers and ordering are maintained within that specific sequence
Multiple event sequences
Section titled “Multiple event sequences”You can create different projections for different event sequences:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record DecFromEventSequencePackageCreated(string PackageId);
[EventType]public record DecFromEventSequencePackageShipped(string PackageId, DateTimeOffset ShippedAt);
[EventType]public record DecFromEventSequencePackageDelivered(string PackageId, DateTimeOffset DeliveredAt);
public record DecFromEventSequenceShipping( string PackageId, DateTimeOffset? ShippedAt, DateTimeOffset? DeliveredAt);
// Projection for order management eventspublic class DecFromEventSequenceMultiOrderProjection : IProjectionFor<DecFromEventSequenceOrder>{ public void Define(IProjectionBuilderFor<DecFromEventSequenceOrder> builder) => builder .FromEventSequence("order-management") .AutoMap() .From<DecFromEventSequenceOrderCreated>(_ => _ .Set(m => m.Status).ToValue(DecFromEventSequenceOrderStatus.Created));}
// Projection for shipping events from a different sequencepublic class DecFromEventSequenceShippingProjection : IProjectionFor<DecFromEventSequenceShipping>{ public void Define(IProjectionBuilderFor<DecFromEventSequenceShipping> builder) => builder .FromEventSequence("shipping-management") .AutoMap() .From<DecFromEventSequencePackageCreated>() .From<DecFromEventSequencePackageShipped>() .From<DecFromEventSequencePackageDelivered>();}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.import { eventType, IProjectionBuilderFor, IProjectionFor, projection } from '@cratis/chronicle';
@eventType()class DecFromEventSequencePackageCreated { packageId = '';}
@eventType()class DecFromEventSequencePackageShipped { packageId = ''; shippedAt = new Date();}
@eventType()class DecFromEventSequencePackageDelivered { packageId = ''; deliveredAt = new Date();}
class DecFromEventSequenceShipping { packageId = ''; shippedAt: Date | null = null; deliveredAt: Date | null = null;}
// Projection for order management events@projection()class DecFromEventSequenceMultiOrderProjection implements IProjectionFor<DecFromEventSequenceOrder> { define(builder: IProjectionBuilderFor<DecFromEventSequenceOrder>): void { builder .fromEventSequence('order-management') .autoMap() .from(DecFromEventSequenceOrderCreated, _ => _ .set(m => m.status).toValue(DecFromEventSequenceOrderStatus.Created)); }}
// Projection for shipping events from a different sequence@projection()class DecFromEventSequenceShippingProjection implements IProjectionFor<DecFromEventSequenceShipping> { define(builder: IProjectionBuilderFor<DecFromEventSequenceShipping>): void { builder .fromEventSequence('shipping-management') .autoMap() .from(DecFromEventSequencePackageCreated) .from(DecFromEventSequencePackageShipped) .from(DecFromEventSequencePackageDelivered); }}When to use FromEventSequence
Section titled “When to use FromEventSequence”Use FromEventSequence() when:
- Bounded contexts: You have separate domains with their own event sequences
- Data partitioning: Events are logically separated by business area or tenant
- Security boundaries: Different sequences have different access requirements
- Performance optimization: You want to reduce the number of events a projection processes
- Legacy integration: You need to process events from specific legacy systems
- Multi-tenant scenarios: Each tenant has their own event sequence
Default behavior
Section titled “Default behavior”If you don’t specify FromEventSequence():
- The projection uses the default
event-logevent sequence. - All events matching the specified types are processed regardless of sequence
- This is suitable for most single-sequence scenarios
Performance considerations
Section titled “Performance considerations”- Specifying an event sequence can improve performance by reducing the number of events processed
- Each sequence maintains its own ordering and sequence numbers
- Consider the volume and frequency of events in each sequence when designing projections
- Event sequence isolation can help with parallel processing and scaling