public void Execute(IList<IAggregateCommand> commands, int expectedVersion) { if (commands.Count == 0) { return; } // readModelBuilderBus will publish events to a bus that will build read models and then pass events off to // the real eventBus var readModelBuilderBus = new ReadModelBuildingEventBus(this.registration.ImmediateReadModels(this.scope), this.eventBus); // holds the events that were raised from the aggregate and will push them to the read model building bus var aggregateEvents = new UnitOfWorkEventBus(readModelBuilderBus); // subscribe to the changes in the aggregate and publish them to aggregateEvents var aggregateRepo = new AggregateRepository(registration.New, this.scope.GetRegisteredObject<IEventStore>(), scope.GetRegisteredObject<ISnapshotRepository>()); var subscription = aggregateRepo.Changes.Subscribe(aggregateEvents.Publish); this.scope.Add(new UnitOfWorkDisposable(subscription)); // add them in this order so that aggregateEvents >> readModelBuilderBus >> read model builder >> eventBus this.scope.Add(aggregateEvents); this.scope.Add(readModelBuilderBus); var cmd = new CommandExecutor(aggregateRepo); cmd.Execute(commands.ToList(), expectedVersion); // enqueue pending commands this.EnqueueCommands(this.scope.GetRegisteredObject<IPendingCommandRepository>(), commands); // enqueue read models to be built - for non-immediate read models var typeName = AggregateRootBase.GetAggregateTypeDescriptor(registration.AggregateType); this.EnqueueReadModels(this.scope.GetRegisteredObject<IReadModelQueueProducer>(), typeName, aggregateEvents.GetEvents().Select(x => x.Event).OfType<IAggregateEvent>().ToList()); }
protected virtual void InternalExecute <T>(IList <IAggregateCommand> commands, int expectedVersion) where T : IAggregateRoot { var registration = this.registrations.FirstOrDefault(x => x.AggregateType == typeof(T)); if (registration == null) { throw new InvalidOperationException(string.Format("Unregistered aggregate type {0}", typeof(T))); } // create a unit of work eventbus to capture aggregate events and read model events and publish in one go using (var busBuffer = new UnitOfWorkEventBus(this.EventBus)) { using (var scope = this.BeginUnitOfWork()) { var exec = new DomainCommandExecutor(scope, registration, busBuffer); exec.Execute(commands, expectedVersion); scope.Commit(); } // get events in the buffer, bundle them up and send them in one go to the background read model builder // finally, publish the events to the real event bus busBuffer.Commit(); } }
private void DoProcessQueue() { // begin read model transaction scope // get events for workitem.AggregateId // pass events to read model // commit // we might see a work item for an aggregate twice in a row // especially if we poke the queue when we receive events on // the event bus // in fact we can subscibe to events on the event bus and do our own poking var doMore = true; while (doMore) { var scope = this.scopeFactory(); using (scope) { var queue = this.queueFactory(scope); var workItems = queue.Peek(this.maxItems); var bus = new UnitOfWorkEventBus(this.context.EventBus); scope.Add(bus); foreach (var workItem in workItems) { if (this.cancel.IsCancellationRequested) { return; } var registration = this.registrations.FirstOrDefault(x => AggregateRootBase.GetAggregateTypeDescriptor(x.AggregateType) == workItem.AggregateType); if (registration != null) { try { this.ProcessWorkItem(scope, registration, bus, workItem); queue.Dequeue(workItem); } catch (Exception ex) { // TODO: handle sqlite busy exceptions var topic = new DomainTopic(registration.AggregateType, workItem.Identity); this.context.EventBus.Publish(new DomainNotification(topic, new ReadModelBuilderFaultedEvent(workItem.Identity, ex))); throw; } } } scope.Commit(); doMore = workItems.Count == this.maxItems; } } }
public void Execute(IList <IAggregateCommand> commands, int expectedVersion) { if (commands.Count == 0) { return; } // readModelBuilderBus will publish events to a bus that will build read models and then pass events off to // the real eventBus var readModelBuilderBus = new ReadModelBuildingEventBus(this.registration.ImmediateReadModels(this.scope), this.eventBus); // holds the events that were raised from the aggregate and will push them to the read model building bus var aggregateEvents = new UnitOfWorkEventBus(readModelBuilderBus); // subscribe to the changes in the aggregate and publish them to aggregateEvents var aggregateRepo = new AggregateRepository(registration.New, this.scope.GetRegisteredObject <IEventStore>(), scope.GetRegisteredObject <ISnapshotRepository>()); var subscription = aggregateRepo.Changes.Subscribe(aggregateEvents.Publish); this.scope.Add(new UnitOfWorkDisposable(subscription)); // add them in this order so that aggregateEvents >> readModelBuilderBus >> read model builder >> eventBus this.scope.Add(aggregateEvents); this.scope.Add(readModelBuilderBus); var cmd = new CommandExecutor(aggregateRepo); cmd.Execute(commands.ToList(), expectedVersion); // enqueue pending commands this.EnqueueCommands(this.scope.GetRegisteredObject <IPendingCommandRepository>(), commands); // enqueue read models to be built - for non-immediate read models var typeName = AggregateRootBase.GetAggregateTypeDescriptor(registration.AggregateType); this.EnqueueReadModels(this.scope.GetRegisteredObject <IReadModelQueueProducer>(), typeName, aggregateEvents.GetEvents().Select(x => x.Event).OfType <IAggregateEvent>().ToList()); }