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); }
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); }
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; } }
protected AggregateRoot(Guid id) : base(id) { _ = ArgumentNotEmpty( id, nameof(id), AggregateRootIdRequired); State = new AggregateState(new SignedVersion(), SignedVersion.Empty); }
public virtual void LoadState(AggregateState state) { if (LoadedEvents != null) { throw new InvalidOperationException(); } Version = state.Version; LoadedEvents = state.Events.ToList(); }
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); }
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; }
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); }
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); }
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); }
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); }
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); }
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); }
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)); }
public TodoTask(TodoTaskId id, AggregateVersion version, AggregateState state) : base(id, version, state) { }
public override void LoadState(AggregateState state) { base.LoadState(state); IsDeleted = true; }
public void LoadState(AggregateState state) { }
public Snapshot(object aggregateId, Type aggregateType, AggregateState aggregateState, Type snapshotBehaviorType, DateTime snapshotTime) : this(Guid.NewGuid(), aggregateId, aggregateType, aggregateState, snapshotBehaviorType, snapshotTime) { }
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); }
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)); }
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) { }
public virtual void LoadState(AggregateState state) { Version = state.Version; }