Projecting by Event Type
While projections can target a specific stream or streams, it is also possible to project by event type. The following sample demonstrates this with a CandleProjection that extends MultiStreamProjection<Candle, Guid> to build Candle aggregates from events of type Tick, grouping every Tick by its CandleId regardless of which stream the event was captured in.
Introduce a type to hold candle data:
public class Candle
{
public Guid Id { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
}This data will then be populated and updated from observing ticks:
// Each Tick carries the identity of the candle it contributes to. The
// ticks can be captured in any number of separate event streams.
public record Tick(Guid CandleId, decimal Price);We then introduce a projection that subscribes to the Tick event:
public partial class CandleProjection: MultiStreamProjection<Candle, Guid>
{
public CandleProjection()
{
// Group every Tick event by its CandleId so that all ticks for the
// same candle are aggregated together, regardless of which stream
// each Tick was captured in. The projection only reacts to events
// of type Tick.
Identity<Tick>(x => x.CandleId);
}
public void Apply(Candle candle, Tick tick)
{
if (candle.Open == 0)
{
candle.Open = tick.Price;
}
candle.High = candle.High == 0 ? tick.Price : Math.Max(candle.High, tick.Price);
candle.Low = candle.Low == 0 ? tick.Price : Math.Min(candle.Low, tick.Price);
candle.Close = tick.Price;
}
}Lastly, we configure the event store to use the newly introduced projection:
var store = DocumentStore.For(opts =>
{
opts.Connection("some connection string");
// Register the projection by event type
opts.Projections.Add<CandleProjection>(ProjectionLifecycle.Inline);
});
