예제 #1
0
        protected internal override void Decode(IChannelHandlerContext context, IRedisMessage message, List <object> output)
        {
            if (message is ArrayHeaderRedisMessage)
            {
                message = this.DecodeRedisArrayHeader((ArrayHeaderRedisMessage)message);
                if (message == null)
                {
                    return;
                }
            }
            else
            {
                ReferenceCountUtil.Retain(message);
            }

            while (this.depths.Count > 0)
            {
                AggregateState current = this.depths.Peek();
                current.Children.Add(message);

                // if current aggregation completed, go to parent aggregation.
                if (current.Children.Count == current.Length)
                {
                    message = new ArrayRedisMessage(current.Children);
                    this.depths.Pop();
                }
                else
                {
                    // not aggregated yet. try next time.
                    return;
                }
            }

            output.Add(message);
        }
예제 #2
0
        private IEventSourcedAggregateRoot ConstructAndLoadEntityFromEvents(Guid aggregateId, IReadOnlyDictionary <string, string> eventStreamMetadata,
                                                                            IReadOnlyCollection <IEventStoreRecord> eventRecords)
        {
            int version = (int)(eventRecords.LastOrDefault()?.StreamSequenceNumber ?? 0);
            var events  = eventRecords.Select(x => x.Event as DomainAggregateEvent
                                              ?? throw new InvalidOperationException(
                                                  $"Cannot load event sourced aggregate ID {aggregateId}: event stream contains non-DomainAggregateEvent events of type {x.Event.GetType().FullName}"))
                          .ToList();

            AggregateState state = new AggregateState(version, events);

            if (!eventStreamMetadata.TryGetValue(AggregateEventStreamMetadataNames.ClassId, out string classIdString))
            {
                throw new InvalidOperationException($"Cannot load event sourced aggregate ID {aggregateId}: aggregate class ID not found in event stream metadata");
            }

            Guid classId    = Guid.Parse(classIdString);
            Type entityType = entityTypeManager.GetClassInfoByClassId(classId).ClrType;

            IEventSourcedAggregateRoot aggregate = (IEventSourcedAggregateRoot)ConstructEntity(entityType, aggregateId);

            aggregate.LoadState(state);

            return(aggregate);
        }
예제 #3
0
            public IEnumerable <IDomainEvent> GenerateSnapshot(AggregateState rehydratedAggregateState)
            {
                var newState = rehydratedAggregateState.GetType().CreateInstance() as AggregateState;
                var events   = rehydratedAggregateState.Events.Where(e => !(e is ThirdEvent));

                newState.ApplyRange(events);
                return(events);
            }
 /// <summary>
 /// Determines the best good point before the end bound.
 /// </summary>
 protected void UpdatePriorPoint(BoundingValue bound, AggregateState state)
 {
     if (state.HasTerminated && (state.LatePoint == null) && bound.PriorPoint == null)
     {
         bound.PriorPoint     = state.PriorPoint;
         bound.PriorBadPoints = state.PriorBadPoints;
         bound.DerivationType = UseSlopedExtrapolation ? BoundingValueType.SlopedExtrapolation : BoundingValueType.SteppedExtrapolation;
     }
 }
예제 #5
0
        protected AggregateRoot(Guid id)
            : base(id)
        {
            _ = ArgumentNotEmpty(
                id,
                nameof(id),
                AggregateRootIdRequired);

            State = new AggregateState(new SignedVersion(), SignedVersion.Empty);
        }
예제 #6
0
            public virtual void LoadState(AggregateState state)
            {
                if (LoadedEvents != null)
                {
                    throw new InvalidOperationException();
                }

                Version      = state.Version;
                LoadedEvents = state.Events.ToList();
            }
예제 #7
0
        private async Task <AggregateState> GetRehydratedAggregateStateAsync(
            object aggregateId,
            Type aggregateType,
            EventStoreDbContext externalCtx = null)
        {
            List <IDomainEvent> events = new List <IDomainEvent>();

#if NETSTANDARD2_0
            events = await
                     GetAllEventsByAggregateId(aggregateType, aggregateId)
                     .ToList().ConfigureAwait(false);
#elif NETSTANDARD2_1
            await foreach (var @event in GetAllEventsByAggregateId(aggregateType, aggregateId))
            {
                events.Add(@event);
            }
#endif
            if (externalCtx != null)
            {
                var eventsInChangeTracker = ExtractEventsFromChangeTracker(externalCtx).Select(GetRehydratedEventFromDbEvent);
                events = events.Concat(eventsInChangeTracker).OrderBy(s => s.Sequence).ToList();
            }
            Snapshot snapshot = null;
            using (var ctx = new EventStoreDbContext(_dbContextOptions, _archiveBehavior))
            {
                var hashedAggregateId = aggregateId.ToJson(true).GetHashCode();
                snapshot = await ctx.Set <Snapshot>()
                           .Where(t => t.AggregateType == aggregateType.AssemblyQualifiedName && t.HashedAggregateId == hashedAggregateId)
                           .FirstOrDefaultAsync().ConfigureAwait(false);
            }

            PropertyInfo   stateProp  = aggregateType.GetAllProperties().FirstOrDefault(p => p.PropertyType.IsSubclassOf(typeof(AggregateState)));
            FieldInfo      stateField = aggregateType.GetAllFields().FirstOrDefault(f => f.FieldType.IsSubclassOf(typeof(AggregateState)));
            Type           stateType  = stateProp?.PropertyType ?? stateField?.FieldType;
            AggregateState state      = null;
            if (stateType != null)
            {
                if (snapshot != null)
                {
                    state = snapshot.SnapshotData.FromJson(stateType) as AggregateState;
                }
                else
                {
                    state = stateType.CreateInstance() as AggregateState;
                }
            }
            else
            {
                throw new InvalidOperationException("EFEventStore.GetRehydratedAggregateAsync() : Cannot find property/field that manage state for aggregate" +
                                                    $" type {aggregateType.FullName}. State should be a property or a field of the aggregate");
            }

            state.ApplyRange(events);
            return(state);
        }
예제 #8
0
        public Snapshot(Guid id, object aggregateId, Type aggregateType, AggregateState aggregateState, Type snapshotBehaviorType, DateTime snapshotTime)
        {
            AggregateId    = aggregateId;
            AggregateType  = aggregateType?.AssemblyQualifiedName ?? throw new ArgumentNullException(nameof(aggregateType));
            AggregateState = aggregateState ?? throw new ArgumentNullException(nameof(aggregateState));

            SnapshotBehaviorType = snapshotBehaviorType.AssemblyQualifiedName ?? throw new ArgumentNullException(nameof(snapshotBehaviorType));
            SnapshotTime         = snapshotTime;

            Id = id;
        }
예제 #9
0
        public Snapshot(Guid id, Guid aggregateId, string aggregateType, AggregateState aggregateState, string snapshotBehaviorType, DateTime snapshotTime)
        {
            AggregateId    = aggregateId;
            AggregateType  = aggregateType ?? throw new ArgumentNullException(nameof(aggregateType));
            AggregateState = aggregateState ?? throw new ArgumentNullException(nameof(aggregateState));

            SnapshotBehaviorType = snapshotBehaviorType ?? throw new ArgumentNullException(nameof(snapshotBehaviorType));
            SnapshotTime         = snapshotTime;

            SnapshotData = AggregateState.ToJson(true);

            Id = id;
        }
        /// <summary>
        /// Returns true if more data is required for the next interval.
        /// </summary>
        public override bool WaitForMoreData(TimeSlice bucket, AggregateState state)
        {
            var wait = false;

            if (!state.HasTerminated)
            {
                if (bucket.ContainsTime(state.LatestTimestamp))
                {
                    wait = true;
                }
            }
            return(wait);
        }
예제 #11
0
        public IEnumerable <IDomainEvent> GenerateSnapshot(AggregateState rehydratedAggregateState)
        {
            IEnumerable <IDomainEvent> snapshotEvents = Enumerable.Empty <IDomainEvent>();

            if (rehydratedAggregateState.Events.All(e => e.Sequence != 0))
            {
                snapshotEvents = rehydratedAggregateState.Events.OrderBy(e => e.Sequence).Take(_eventCount);
            }
            else
            {
                snapshotEvents = rehydratedAggregateState.Events.OrderBy(e => e.EventTime).Take(_eventCount);
            }

            return(snapshotEvents);
        }
예제 #12
0
        GenerateSnapshotAsync(Guid aggregateId, Type aggregateType, IEventSourcedAggregate rehydratedAggregate)
        {
            Snapshot            snap        = null;
            const int           newSequence = 1;
            List <IDomainEvent> events      = new List <IDomainEvent>();

            events = EventStoreAzureDbContext.Client.CreateDocumentQuery <Event>(EventStoreAzureDbContext.EventsDatabaseLink)
                     .Where(@event => @event.AggregateId == aggregateId && @event.AggregateType == aggregateType.AssemblyQualifiedName)
                     .Select(EventStoreManager.GetRehydratedEventFromDbEvent).ToList();

            object stateProp =
                aggregateType.GetAllProperties().FirstOrDefault(p => p.PropertyType.IsSubclassOf(typeof(AggregateState)))
                ??
                (object)aggregateType.GetAllFields().FirstOrDefault(f => f.FieldType.IsSubclassOf(typeof(AggregateState)));

            AggregateState state = null;

            if (stateProp is PropertyInfo propInfo)
            {
                state = propInfo.GetValue(rehydratedAggregate) as AggregateState;
            }
            else if (stateProp is FieldInfo fieldInfo)
            {
                state = fieldInfo.GetValue(rehydratedAggregate) as AggregateState;
            }

            if (state != null)
            {
                snap = new Snapshot(
                    aggregateId: aggregateId,
                    aggregateType: aggregateType.AssemblyQualifiedName,
                    aggregateState: state,
                    snapshotBehaviorType: typeof(NumericSnapshotBehavior).AssemblyQualifiedName,
                    snapshotTime: DateTime.Now);

                var feedResponse = await EventStoreAzureDbContext.Client.CreateDocumentQuery <Event>(EventStoreAzureDbContext.EventsDatabaseLink)
                                   .Where(@event => @event.AggregateId == aggregateId && @event.AggregateType == aggregateType.AssemblyQualifiedName)
                                   .AsDocumentQuery().ExecuteNextAsync <Document>().ConfigureAwait(false);

                await feedResponse
                .DoForEachAsync(async e => await EventStoreAzureDbContext.Client.DeleteDocumentAsync(documentLink : e.SelfLink).ConfigureAwait(false))
                .ConfigureAwait(false);
            }
            return(snap, newSequence, events);
        }
예제 #13
0
        protected async Task <TAggregate> Load <TAggregate>(string aggregateId, bool isReadOnly = false) where TAggregate : AggregateState, new()
        {
            var stream = await _store.ReadStreamForwards(new StreamId(aggregateId), 0, int.MaxValue, true);

            var events = new List <object>();

            foreach (var message in stream.Messages)
            {
                var @event = await Deserialize(message);

                events.Add(@event);
            }
            //var events = await Task.WhenAll(stream.Messages.Select(Deserialize));
            var aggregate = AggregateState.Build <TAggregate>(aggregateId, stream.LastStreamVersion, isReadOnly, events);

            if (!isReadOnly)
            {
                _trackedAggregates.Add(aggregate);
            }
            return(aggregate);
        }
        public IEventSourcedAggregateRoot ConstructAndLoadEntityFromEvents(Guid aggregateId, IReadOnlyDictionary <string, string> eventStreamMetadata,
                                                                           IReadOnlyCollection <IEventStoreRecord> eventRecords)
        {
            if (!eventStreamMetadata.TryGetValue(AggregateEventStreamMetadataNames.ClassId, out string classIdString))
            {
                throw new InvalidOperationException($"Cannot load event sourced aggregate ID {aggregateId}: aggregate class ID not found in event stream metadata");
            }

            Guid classId    = Guid.Parse(classIdString);
            Type entityType = entityTypeManager.GetClassInfoByClassId(classId).ClrType;

            IEnumerable <IEventMessage <DomainAggregateEvent> > eventMessages;

            try
            {
                eventMessages = eventRecords.Select(EventStoreEventMessage.FromRecord)
                                .Cast <IEventMessage <DomainAggregateEvent> >();
            }
            catch (InvalidCastException e)
            {
                throw new InvalidOperationException(
                          $"Cannot load event sourced aggregate ID {aggregateId}: event stream contains non-DomainAggregateEvent events",
                          e);
            }

            var upgradedEvents = eventStreamUpgrader.UpgradeStream(eventMessages, eventStreamMetadata);
            var events         = upgradedEvents.Select(x => x.Event).ToArray();

            int version = (int)(eventRecords.LastOrDefault()?.AdditionalMetadata.GetAggregateVersion() // use non-upgraded event records to preserve the versions
                                ?? eventRecords.LastOrDefault()?.StreamSequenceNumber
                                ?? 0);

            AggregateState state = new AggregateState(version, events);

            IEventSourcedAggregateRoot aggregate = (IEventSourcedAggregateRoot)ConstructEntity(entityType, aggregateId);

            aggregate.LoadState(state);

            return(aggregate);
        }
예제 #15
0
        private async Task <AggregateState> GetRehydratedStateAsync <TId>(TId aggregateUniqueId, Type aggregateType)
        {
            var snapshot = await GetSnapshotFromAggregateId(aggregateUniqueId, aggregateType).ConfigureAwait(false);

            PropertyInfo   stateProp  = aggregateType.GetAllProperties().FirstOrDefault(p => p.PropertyType.IsSubclassOf(typeof(AggregateState)));
            FieldInfo      stateField = aggregateType.GetAllFields().FirstOrDefault(f => f.FieldType.IsSubclassOf(typeof(AggregateState)));
            Type           stateType  = stateProp?.PropertyType ?? stateField?.FieldType;
            AggregateState state      = null;

            if (stateType != null)
            {
                if (snapshot != null)
                {
                    state = snapshot.AggregateState;
                }
                else
                {
                    state = stateType.CreateInstance() as AggregateState;
                }
            }
            else
            {
                throw new InvalidOperationException("MongoDbEventStore.GetRehydratedAggregateAsync() : Cannot find property/field that manage state for aggregate" +
                                                    $" type {aggregateType.FullName}. State should be a property or a field of the aggregate");
            }
            List <IDomainEvent> events = new List <IDomainEvent>();

#if NETSTANDARD2_0
            events = await GetAllEventsByAggregateId(aggregateType, aggregateUniqueId).ToList().ConfigureAwait(false);
#elif NETSTANDARD2_1
            await foreach (var @event in GetAllEventsByAggregateId(aggregateType, aggregateUniqueId))
            {
                events.Add(@event);
            }
#endif
            state.ApplyRange(events);
            return(state);
        }
예제 #16
0
        private async Task <TBase> LoadAggregateAsync(Guid aggregateId)
        {
            IReadOnlyDictionary <string, string> eventStreamMetadata;

            try
            {
                eventStreamMetadata = await eventStore.GetStreamMetadataAsync(aggregateId);
            }
            catch (EntityNotFoundException e)
            {
                return(null);
            }

            var eventRecords = await eventStore.GetEventsAsync(aggregateId);

            int version = (int)(eventRecords.LastOrDefault()?.StreamSequenceNumber ?? 0);
            var events  = eventRecords.Select(x => x.Event as DomainAggregateEvent
                                              ?? throw new InvalidOperationException(
                                                  $"Cannot load event sourced aggregate ID {aggregateId}: event stream contains non-DomainAggregateEvent events of type {x.Event.GetType().FullName}"))
                          .ToList();

            AggregateState state = new AggregateState(version, events);

            if (!eventStreamMetadata.TryGetValue(AggregateEventStreamMetadataNames.ClassId, out string classIdString))
            {
                throw new InvalidOperationException($"Cannot load event sourced aggregate ID {aggregateId}: aggregate class ID not found in event stream metadata");
            }

            Guid classId    = Guid.Parse(classIdString);
            Type entityType = entityTypeManager.GetClassInfoByClassId(classId).ClrType;

            TBase aggregate = (TBase)ConstructEntity(entityType, aggregateId);

            aggregate.LoadState(state);

            return(aggregate);
        }
예제 #17
0
        private static AggregateState GetState(List <Event> eventsData, AggregateState state)
        {
            if (eventsData.Count == 0)
            {
                return(state);
            }
            var @event = eventsData.First();

            void AddNameInfo(string name, string last)
            {
                state.Name     = name;
                state.LastName = last;
            }

            switch (@event)
            {
            case PersonalDetailsChanged pdc:
                AddNameInfo(pdc.Name, pdc.Last);
                break;

            case PersonalDetailsCreated pd:
                AddNameInfo(pd.Name, pd.Last);
                break;

            case PhoneInfoCreated pi:
                state.PhoneInfo = pi.PhoneInfo;
                break;

            case PhoneInfoChanged pc:
                state.PhoneInfo = pc.PhoneInfo;     //now abstraction comes handy
                break;
            }

            eventsData.RemoveAt(0);
            return(GetState(eventsData, state));
        }
예제 #18
0
 public TodoTask(TodoTaskId id, AggregateVersion version, AggregateState state) : base(id, version, state)
 {
 }
예제 #19
0
 public override void LoadState(AggregateState state)
 {
     base.LoadState(state);
     IsDeleted = true;
 }
예제 #20
0
 public void LoadState(AggregateState state)
 {
 }
예제 #21
0
 public Snapshot(object aggregateId, Type aggregateType, AggregateState aggregateState, Type snapshotBehaviorType, DateTime snapshotTime)
     : this(Guid.NewGuid(), aggregateId, aggregateType, aggregateState, snapshotBehaviorType, snapshotTime)
 {
 }
예제 #22
0
        public async Task <IEventSourcedAggregate> GetRehydratedAggregateAsync(
            object aggregateUniqueId, Type aggregateType)
        {
            if (aggregateUniqueId == null)
            {
                throw new ArgumentNullException(nameof(aggregateUniqueId));
            }
            var aggInstance = aggregateType.CreateInstance() as IEventSourcedAggregate;

            if (aggInstance == null)
            {
                throw new InvalidOperationException("EFEventStore.GetRehydratedAggregateAsync() : Cannot create a new instance of" +
                                                    $" {aggregateType.FullName} aggregate. It should have one parameterless constructor (can be private).");
            }
            Snapshot snapshot = null;

            using (var ctx = new EventStoreDbContext(_dbContextOptions, _archiveBehavior))
            {
                var hashedAggregateId = aggregateUniqueId.ToJson(true).GetHashCode();
                snapshot = await ctx.Set <Snapshot>()
                           .Where(t => t.AggregateType == aggregateType.AssemblyQualifiedName && t.HashedAggregateId == hashedAggregateId)
                           .FirstOrDefaultAsync().ConfigureAwait(false);
            }
            PropertyInfo stateProp  = aggregateType.GetAllProperties().FirstOrDefault(p => p.PropertyType.IsSubclassOf(typeof(AggregateState)));
            FieldInfo    stateField = aggregateType.GetAllFields().FirstOrDefault(f => f.FieldType.IsSubclassOf(typeof(AggregateState)));
            Type         stateType  = stateProp?.PropertyType ?? stateField?.FieldType;

            if (stateType != null)
            {
                AggregateState state = stateType.CreateInstance() as AggregateState;
                if (snapshot != null)
                {
                    state = snapshot.SnapshotData.FromJson(stateType) as AggregateState;
                    if (state == null)
                    {
                        throw new InvalidOperationException("EFEventStore.GetRehydratedAggregateAsync() : Cannot retrieve a valid state from snapshot in database.");
                    }
                }
                if (stateProp != null)
                {
                    stateProp.SetValue(aggInstance, state);
                }
                else
                {
                    stateField.SetValue(aggInstance, state);
                }
            }
            else
            {
                throw new InvalidOperationException("EFEventStore.GetRehydratedAggregateAsync() : Cannot find property/field that manage state for aggregate" +
                                                    $" type {aggregateType.FullName}. State should be a property or a field of the aggregate");
            }
            List <IDomainEvent> events = new List <IDomainEvent>();

#if NETSTANDARD2_0
            events = await
                     GetAllEventsByAggregateId(aggregateType, aggregateUniqueId)
                     .ToList().ConfigureAwait(false);
#elif NETSTANDARD2_1
            await foreach (var @event in GetAllEventsByAggregateId(aggregateType, aggregateUniqueId))
            {
                events.Add(@event);
            }
#endif
            aggInstance.RehydrateState(events);
            return(aggInstance);
        }
예제 #23
0
        private async Task <AppendResult> Save(AggregateState aggregate, Guid transactionId)
        {
            var toAppend = aggregate.EmitUncommittedEvents(transactionId).Select(Serialize).ToArray();

            return(await _store.AppendToStream(aggregate.Id.ToString(), aggregate.LoadedRevision, toAppend));
        }
예제 #24
0
 public Snapshot(Guid aggregateId, string aggregateType, AggregateState aggregateState, string snapshotBehaviorType, DateTime snapshotTime)
     : this(Guid.NewGuid(), aggregateId, aggregateType, aggregateState, snapshotBehaviorType, snapshotTime)
 {
 }
 /// <summary>
 /// Updates the bounding values for the time slice.
 /// </summary>
 public override void UpdateBoundingValues(TimeSlice bucket, AggregateState state)
 {
 }
예제 #26
0
 public virtual void LoadState(AggregateState state)
 {
     Version = state.Version;
 }