public async Task A_stream_can_be_derived_from_an_index_projection_in_order_to_perform_a_reduce_operation() { store.WriteEvents(i => new AccountOpened { AccountType = i % 2 == 0 ? BankAccountType.Checking : BankAccountType.Savings }, howMany: 100); var eventsByAggregate = streamSource.StreamPerAggregate() .Trace() .Map(ss => ss.Select(s => s.Trace())); var indexCatchup = StreamCatchup.All(eventsByAggregate, batchSize: 1); var index = new Projection <ConcurrentBag <AccountOpened>, string> { Value = new ConcurrentBag <AccountOpened>() }; // subscribe a catchup to the updates stream to build up an index indexCatchup.Subscribe( Aggregator.Create <Projection <ConcurrentBag <AccountOpened> >, IDomainEvent>(async(p, events) => { foreach (var e in events.OfType <AccountOpened>() .Where(e => e.AccountType == BankAccountType.Savings)) { p.Value.Add(e); } return(p); }).Trace(), async(streamId, aggregate) => await aggregate(index)); // create a catchup over the index var savingsAccounts = Stream.Create <IDomainEvent, string>( "Savings accounts", async q => index.Value.SkipWhile(v => q.Cursor.HasReached(v.CheckpointToken)) .Take(q.BatchSize ?? 1000)); var savingsAccountsCatchup = StreamCatchup.Create(savingsAccounts); var numberOfSavingsAccounts = new Projection <int, int>(); savingsAccountsCatchup.Subscribe <Projection <int, int>, IDomainEvent, string>( manageProjection: async(streamId, aggregate) => { numberOfSavingsAccounts = await aggregate(numberOfSavingsAccounts); }, aggregate: async(c, es) => { c.Value += es.Count; return(c); }); using (indexCatchup.Poll(TimeSpan.FromMilliseconds(100))) using (savingsAccountsCatchup.Poll(TimeSpan.FromMilliseconds(50))) { await Wait.Until(() => numberOfSavingsAccounts.Value >= 50); } }
public async Task When_an_aggregation_fails_then_the_projection_is_not_updated() { var projections = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace()); // subscribe a flaky projector catchup.Subscribe(new BalanceProjector() .Pipeline(async(projection, batch, next) => { bool shouldThrow = true; // declared outside to nudge the compiler into inferring the correct .Pipeline overload if (shouldThrow) { throw new Exception("oops"); } await next(projection, batch); }), projections.AsHandler(), e => e.Continue()); await catchup.RunSingleBatch(); projections.Count().Should().Be(0); }
public async Task Catchup_can_use_a_sequence_of_keys_to_traverse_all_aggregates() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate()); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Sum(b => b.Balance) .Should() .Be(100); projectionStore.Count() .Should() .Be(100); }