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());
        }
Esempio n. 2
0
        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());
        }
        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;
                }
            }
        }