protected void RaiseEvent(Guid aggregateId, IAggregateEvent evt) { if (aggregateId == Guid.Empty) { throw new ArgumentNullException("aggregateId", "Cannot raise an event without specifying the correct aggregate id"); } if (this.Identity == Guid.Empty) { this.Identity = aggregateId; } if (this.Identity != aggregateId) { throw new InvalidOperationException("Cannot raise an event for a different aggregate root id"); } this.Version++; evt.Identity = Guid.NewGuid(); evt.AggregateId = aggregateId; evt.Version = this.Version; evt.Timestamp = DateTime.UtcNow; this.ApplyEvent(evt); this.uncommittedEvents.Add(evt); }
public void CanCreateFromEvents() { // arrange var factory = AggregateFactory.For <Counter>(); var aggregateId = "some id"; var events = new IAggregateEvent[] { new AggregateEvent <CounterIncremented>( aggregateId, 1, DateTime.UtcNow, new CounterIncremented { Increment = 1 }), new AggregateEvent <CounterMultiplied>( aggregateId, 2, DateTime.UtcNow, new CounterMultiplied { Factor = 2 }), new AggregateEvent <CounterIncremented>( aggregateId, 3, DateTime.UtcNow, new CounterIncremented { Increment = 5 }), }; // act var aggregate = factory.CreateFromEvents(aggregateId, events); // assert Assert.NotNull(aggregate); Assert.AreEqual(aggregateId, aggregate.Id); Assert.AreEqual(events.Last().SequenceId, aggregate.OriginalVersion); Assert.AreEqual(0, aggregate.Changes.Count()); Assert.NotNull(aggregate.State); // no changes, so state version is the same Assert.AreEqual(events.Last().SequenceId, aggregate.State.Version); }
protected virtual void ApplyEvent(IAggregateEvent <TAggregate, TIdentity> aggregateEvent) { var eventType = aggregateEvent.GetType(); if (_eventHandlers.ContainsKey(eventType)) { _eventHandlers[eventType](aggregateEvent); } else if (_eventAppliers.Any(ea => ea.Apply((TAggregate)this, aggregateEvent))) { // Already done } else { Action <TAggregate, IAggregateEvent> applyMethod; if (!ApplyMethods.TryGetValue(eventType, out applyMethod)) { throw new NotImplementedException( $"Aggregate '{Name}' does have an 'Apply' method that takes aggregate event '{eventType.PrettyPrint()}' as argument"); } applyMethod(this as TAggregate, aggregateEvent); } Version++; }
private void PublishToSubscribers(IAggregateEvent @event, List <Task> tasks) { foreach (var type in this.registry.GetSubscribersForEvent(@event)) { this.PublishToSubscriber(@event, tasks, type); } }
private void PublishToSubscriber(IAggregateEvent @event, List <Task> tasks, Type type) { var notification = @event.ToEventPublishedNotification(type); var msg = new CloudQueueMessage(JsonConvert.SerializeObject(notification)); tasks.Add(this.queue.AddMessageAsync(msg)); }
public IEnumerable <KeyValuePair <string, string> > ProvideMetadata <TAggregate, TIdentity>( TIdentity id, IAggregateEvent aggregateEvent, IMetadata metadata) where TAggregate : IAggregateRoot <TIdentity> where TIdentity : IIdentity { yield return(new KeyValuePair <string, string>("remote_ip_address", _owinContext.Request.RemoteIpAddress)); var headerInfo = HeaderPriority .Select(h => { string[] value; var address = _owinContext.Request.Headers.TryGetValue(h, out value) ? string.Join(string.Empty, value) : string.Empty; return(new { Header = h, Address = address }); }) .FirstOrDefault(a => !string.IsNullOrEmpty(a.Address)); if (headerInfo == null) { yield break; } yield return(new KeyValuePair <string, string>("user_host_address", headerInfo.Address)); yield return(new KeyValuePair <string, string>("user_host_address_source_header", headerInfo.Header)); }
public void TakeSnapshot(Guid aggregateId) { // arrange; var savedEvents = new IAggregateEvent <Guid>[] { new GiftCardCreated(aggregateId, DateTime.UtcNow.AddDays(-10), 100), new GiftCardDebited(aggregateId, 2, DateTime.UtcNow.AddDays(-5), 30), new GiftCardDebited(aggregateId, 3, DateTime.UtcNow.AddDays(-2), 20) }; var sut = new GiftCard(aggregateId, savedEvents); // act sut.TakeSnapshot(); // assert Assert.Equal(3, sut.Version); IAggregateChangeset <Guid> changeset = sut.GetChangeset(); Assert.NotNull(changeset.Snapshot); Assert.IsType <GiftCardSnapshot>(changeset.Snapshot); var snapshot = changeset.Snapshot as GiftCardSnapshot; Assert.Equal(aggregateId, snapshot.AggregateId); Assert.Equal(3, snapshot.AggregateVersion); Assert.Equal(sut.Balance, snapshot.Balance); }
public bool Apply(MyAggregate aggregate, IAggregateEvent <MyAggregate, MyId> aggregateEvent) { var myCountEvent = (MyCountEvent)aggregateEvent; Count += myCountEvent.Count; return(true); }
public IEnumerable <string> GetReadModelIds(IDomainEvent domainEvent) { IAggregateEvent aggregateEvent = domainEvent.GetAggregateEvent(); switch (aggregateEvent) { case InvitationAdded @event: yield return(@event.InvitationId.Value); break; case InvitationDeleted @event: yield return(@event.InvitationId.Value); break; case InvitationAccepted @event: yield return(@event.InvitationId.Value); break; case InvitationRejected @event: yield return(@event.InvitationId.Value); break; } }
public async Task OnEvent(IAggregateEvent ev, string queryId = null) { queryId = queryId ?? ev.AggregateId; var query = await queryStore.Get(queryId); var data = query.Data; if (query.Version >= ev.SequenceId) { // event already applied return; } int originalVersion = query.Version; if (originalVersion < ev.SequenceId - 1) { // fastforward to the current event's version query = await FastForward(query, ev.SequenceId); } else { // query is sync, just apply the event query.Data = mutator.InvokeEventMutator(data, ev); query.Version = ev.SequenceId; query.DateUtc = DateTime.UtcNow; } await queryStore.Set(queryId, query, originalVersion); }
public EventWrapper(IAggregateEvent @event, int eventNumber, string streamStateId) { Event = @event; EventNumber = eventNumber; EventStreamId = streamStateId; Id = $"{streamStateId}-{EventNumber}"; }
public void WhenNewEvents_StateChanges() { // arrange var factory = AggregateFactory.For <Counter>(); var aggregateId = "some id"; var events = new IAggregateEvent[] { new AggregateEvent <CounterIncremented>(aggregateId, 1, DateTime.UtcNow, new CounterIncremented { Increment = 1 }), new AggregateEvent <CounterMultiplied>(aggregateId, 2, DateTime.UtcNow, new CounterMultiplied { Factor = 2 }), }; var counter = factory.CreateFromEvents(aggregateId, events); // act counter.Increment(5); // assert Assert.AreEqual(2, counter.OriginalVersion); Assert.AreEqual(3, counter.State.Version); Assert.AreEqual(1, counter.Changes.Count()); Assert.AreEqual(5, counter.Changes .OfType <IAggregateEvent <CounterIncremented> >() .First().Payload.Increment); }
protected virtual void ApplyEvent(IAggregateEvent <TAggregate, TIdentity> aggregateEvent) { if (aggregateEvent == null) { throw new ArgumentNullException(nameof(aggregateEvent)); } var eventType = aggregateEvent.GetType(); if (_eventHandlers.ContainsKey(eventType)) { _eventHandlers[eventType](aggregateEvent); } else if (_eventAppliers.Any(ea => ea.Apply((TAggregate)this, aggregateEvent))) { // Already done } else { if (applyMethods.Value.TryGetValue(eventType, out var applyMethod)) { applyMethod(this as TAggregate, aggregateEvent); } else { throw new NotImplementedException( $"Aggregate '{aggregateName.Value}' does have an 'Apply' method that takes aggregate event '{eventType.PrettyPrint()}' as argument"); } } }
private void ApplyEvent(IAggregateEvent aggregateEvent) { var applyMethod = GetApplyMethod(aggregateEvent.GetType()); applyMethod(this as TAggregate, aggregateEvent); Version++; }
public IEnumerable <KeyValuePair <string, string> > ProvideMetadata <TAggregate, TIdentity>( TIdentity id, IAggregateEvent aggregateEvent, IMetadata metadata) where TAggregate : IAggregateRoot <TIdentity> where TIdentity : IIdentity { //var headerInfo = HeaderPriority // .Select(h => // { // StringValues value; // var address = _httpContextAccessor.HttpContext.Request.Headers.TryGetValue(h, out value) // ? string.Join(string.Empty, value) // : string.Empty; // return new { Header = h, Address = address }; // }) // .FirstOrDefault(a => !string.IsNullOrEmpty(a.Address)); //if (headerInfo == null) //{ // yield break; //} string tenantKey = "tenant_Id"; yield return(new KeyValuePair <string, string>(tenantKey, "tenant-" + _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString().Replace(".", ""))); }
public async Task <IAggregateEvent <TKey>[]> GetEventsAsync( TKey aggregateId, int minVersion, int maxVersion, CancellationToken cancellationToken = default) { var filter = new QueryFilter("aggregateVersion", QueryOperator.Between, minVersion, maxVersion); Search search = this._table.Query(aggregateId as dynamic, filter); var events = new List <IAggregateEvent <TKey> >(); do { List <Document> documents = await search.GetNextSetAsync(cancellationToken).ConfigureAwait(false); foreach (Document document in documents) { string json = document.ToJson(); IAggregateEvent <TKey> @event = JsonConvert.DeserializeObject <IAggregateEvent <TKey> >(json, Defaults.JsonSerializerSettings); events.Add(@event); } } while (!search.IsDone); return(events.OrderBy(x => x.AggregateVersion).ToArray()); }
public void ApplyEvent(IAggregateEvent @event) { if (this.Id != @event.AggregateId) { return; } if (this.Version + 1 != @event.Version) { throw new InvalidOperationException(); } this.Version = @event.Version; if (@event.Version == 1) { this.ShardKey = @event.ShardKey; } else if (this.ShardKey != @event.ShardKey) { throw new InvalidOperationException(); } this.CallApply(@event); this._changes.Add(@event); }
public void CanCreateFromEvents() { // arrange var factory = AggregateFactory.For<Counter>(); var aggregateId = "some id"; var events = new IAggregateEvent[] { new AggregateEvent<CounterIncremented>( aggregateId, 1, DateTime.UtcNow, new CounterIncremented { Increment = 1 }), new AggregateEvent<CounterMultiplied>( aggregateId, 2, DateTime.UtcNow, new CounterMultiplied{ Factor = 2}), new AggregateEvent<CounterIncremented>( aggregateId, 3, DateTime.UtcNow, new CounterIncremented { Increment = 5 }), }; // act var aggregate = factory.CreateFromEvents(aggregateId, events); // assert Assert.NotNull(aggregate); Assert.AreEqual(aggregateId, aggregate.Id); Assert.AreEqual(events.Last().SequenceId, aggregate.OriginalVersion); Assert.AreEqual(0, aggregate.Changes.Count()); Assert.NotNull(aggregate.State); // no changes, so state version is the same Assert.AreEqual(events.Last().SequenceId, aggregate.State.Version); }
//[DebuggerStepThrough] public void InvokeHandler(IAggregateEvent e, EntityBase entity) { if (eventMutatesEntityState) { handler.Invoke(entity, new object[] {e}); } }
private void SetId(int nextExpectedId, IAggregateEvent @event) { if (nextExpectedId == 1) { this.aggregate.idStrategy.SetId(@event.Id); } }
protected void CheckIsCreated(IAggregateEvent @event) { if (Id == new Guid() && (!(@event is IAggregateCreationEvent))) { throw new Exception("The object has not been 'created' yet!"); } }
private void ApplyEvent(IAggregateEvent evt) { if (!MethodExecutor.ExecuteMethodForSingleParam(this, evt)) { throw new MissingMethodException(string.Format("Aggregate {0} does not support a method that can be called with {1}", this, evt)); } }
private void LoadEvent(IAggregateEvent @event, int nextExpectedId) { this.EnsureEventIsTheExpectedVersion(@event, nextExpectedId); this.EnsureEventIsForTheCorrectAggregate(nextExpectedId, @event); this.SetId(nextExpectedId, @event); this.InvokeReducer(@event); }
public void Register(IAggregateEvent @event) { if (Aggregate != @event.Aggregate || CurrentVersion != @event.AggregateVersion) throw new ConcurrencyException(Aggregate); Events.Add(@event); CurrentVersion = @event.Aggregate.Version; }
private void EnsureEventIsForTheCorrectAggregate(int nextExpectedId, IAggregateEvent @event) { if (nextExpectedId > 1 && @event.Id != this.aggregate.Id) { throw new UnexpectedEventException( $"Aggregate {this.aggregate.GetType().Name} expected to load an event of Id {this.aggregate.Id} but received an event of type {@event.GetType().Name} with an id of {@event.Id}"); } }
private void EnsureEventIsTheExpectedVersion(IAggregateEvent @event, int nextExpectedId) { if (@event.Version != nextExpectedId) { throw new UnexpectedEventException( $"Aggregate {this.aggregate.GetType().Name} expected to load an event of version {nextExpectedId} but was {@event.Version}"); } }
protected virtual void ApplyEvent(IAggregateEvent <TAggregateSaga, TIdentity> aggregateEvent) { var eventApplier = GetEventApplyMethods(aggregateEvent); eventApplier(aggregateEvent); Version++; }
public async Task AddEventAsync( IAggregateEvent <TKey> @event, CancellationToken cancellationToken = default) { string json = JsonConvert.SerializeObject(@event, Defaults.JsonSerializerSettings); var item = Document.FromJson(json); await _table.PutItemAsync(item, cancellationToken).ConfigureAwait(false); }
public Task Put <T>(T id, IAggregateEvent <T> @event) where T : IIdentity { var events = _db.GetOrAdd(id, new HashSet <IAggregateEvent>()); events.Add(@event); return(Task.CompletedTask); }
public static T Is <T>(this IAggregateEvent aggregateEvent, Func <T, bool> f) where T : class, IAggregateEvent { T @event = aggregateEvent as T; @event.ShouldNotBeNull("Is another type."); f(@event).ShouldBeTrue("Is verification is not true."); return(@event); }
public void Commit() { for (int i = 0; i < Events.Count(); i++) { IAggregateEvent <TKey> @evt = _aggregate._unsavedEvents.Dequeue(); _aggregate._savedEvents.Enqueue(@evt); } }
public static DynamicTableEntity ToEntity(this IAggregateEvent <JObject> ev) { var entity = new DynamicTableEntity(ev.AggregateId, ev.SequenceId.ToString()); entity.Properties["DateUtc"] = new EntityProperty(ev.DateUtc); entity.Properties["Name"] = new EntityProperty(ev.Name); entity.Properties["Payload"] = new EntityProperty(ev.Payload.ToString()); return(entity); }
public EventHistory(IAggregateEvent @event) { EventType = @event.GetType().Name; Version = @event.Version; Event = Singleton <IJsonConvert> .Instance.SerializeObject(@event); AggregateId = @event.AggregateId; CreationTime = DateTime.UtcNow; }
public void UpdateEventDetails(IAggregateEvent e, IAggregate aggregate, EntityBase source) { versionProperty.SetValue(e, aggregate.GetVersion()); aggregateIdentityProperty.SetValue(e, aggregate.Identity.GetId()); if (entityIdentityProperty != null) { entityIdentityProperty.SetValue(e, source.Identity.GetId()); } }
public bool EventIsOwnedByEntity(IAggregateEvent e, EntityBase entity) { if (entity.GetType() != entityType) return false; if (e.GetType() != eventType) return false; if (entityIdentityProperty == null) { object identity = aggregateIdentityProperty.GetValue(e); return entity.Identity.Equals(identity); } else { object identity = entityIdentityProperty.GetValue(e); return entity.Identity.Equals(identity); } }
protected internal override void SaveEvent(IAggregateEvent @event, EntityBase source) { Parent.SaveEvent(@event, source); }
public bool CanHandleEvent(IAggregateEvent e) { return e.GetType() == eventType; }
bool IEntity.ApplyEvent(IAggregateEvent @event) { return ApplyEvent(@event, ApplyEventAs.Historical); }
public void CannotCreateFromUnknownEvents() { // arrange var factory = AggregateFactory.For<Counter>(); var aggregateId = "some id"; var events = new IAggregateEvent[] { new AggregateEvent<CounterIncremented>(aggregateId, 1, DateTime.UtcNow, new CounterIncremented { Increment = 1 }), new AggregateEvent<EventThatNoAggregateKnows>(aggregateId, 2, DateTime.UtcNow, new EventThatNoAggregateKnows()), }; // act and assert try { // TODO: should throw a specific known exception for this scenario factory.CreateFromEvents(aggregateId, events); Assert.Fail("should not have been possible to create the aggregate using an unknown event"); } catch (Exception) { Assert.Pass(); } }
public EventHandlerNotFoundException(EntityBase entity, IAggregateEvent @event) : base(GetErrorMessage(entity, @event)) { }
public static void Raise(IAggregateEvent e) { var eventBus = ServiceLocator.Current.GetInstance<IInMemoryEventBus>(); eventBus.Raise(e); }
protected internal virtual bool ApplyEvent(IAggregateEvent @event, ApplyEventAs applyEventAs) { try { EventHandlerProperties handler = applyEventAs == ApplyEventAs.Historical ? eventHandlers.FirstOrDefault(h => h.EventIsOwnedByEntity(@event, this)) : eventHandlers.FirstOrDefault(h => h.CanHandleEvent(@event)); if (handler != null) { handler.InvokeHandler(@event, this); return true; } } catch (TargetInvocationException ex) { throw new EventHandlerInvocationException(this, @event, ex); } return false; }
public bool ConflictsWith(IAggregateEvent otherEvent) { return false; }
protected internal void RaiseEvent(IAggregateEvent @event) { if (ApplyEvent(@event, ApplyEventAs.New)) { SaveEvent(@event, this); return; } throw new EventHandlerNotFoundException(this, @event); }
protected internal abstract void SaveEvent(IAggregateEvent @event, EntityBase source);
private static string GetErrorMessage(EntityBase entity, IAggregateEvent @event) { return String.Format("An error occured while invoking event handler {0} on {1}", @event.GetType().FullName, entity.GetType().FullName); }
private static string GetErrorMessage(EntityBase entity, IAggregateEvent @event) { return String.Format("Unable to locate an event handler for {0} on {1}", @event.GetType().FullName, entity.GetType().FullName); }
public static IDomainNotification CreateNotification(Type aggregateRootType, IAggregateEvent domainEvent) { return new DomainNotification(new DomainTopic(aggregateRootType, domainEvent.AggregateId), domainEvent); }
public EventHandlerInvocationException(EntityBase entity, IAggregateEvent @event, TargetInvocationException ex) : base(GetErrorMessage(entity, @event), ex.InnerException ?? ex) { }
public StoredEvent(IAggregateEvent<JObject> source, string transactionId, bool committed) { this.Source = source; this.TransactionId = transactionId; this.Committed = committed; }
public void WhenNewEvents_StateChanges() { // arrange var factory = AggregateFactory.For<Counter>(); var aggregateId = "some id"; var events = new IAggregateEvent[] { new AggregateEvent<CounterIncremented>(aggregateId, 1, DateTime.UtcNow, new CounterIncremented { Increment = 1 }), new AggregateEvent<CounterMultiplied>(aggregateId, 2, DateTime.UtcNow, new CounterMultiplied{ Factor = 2}), }; var counter = factory.CreateFromEvents(aggregateId, events); // act counter.Increment(5); // assert Assert.AreEqual(2, counter.OriginalVersion); Assert.AreEqual(3, counter.State.Version); Assert.AreEqual(1, counter.Changes.Count()); Assert.AreEqual(5, counter.Changes .OfType<IAggregateEvent<CounterIncremented>>() .First().Payload.Increment); }
private Task HandleEvent(CounterQueryEventsHandler handler, IAggregateEvent ev) { if (ev is IAggregateEvent<Sample.CounterIncremented>) { return handler.On(ev as IAggregateEvent<Sample.CounterIncremented>); } else if (ev is IAggregateEvent<Sample.CounterMultiplied>) { return handler.On(ev as IAggregateEvent<Sample.CounterMultiplied>); } else { return handler.On(ev as IAggregateEvent<Sample.CounterReset>); } }
public void Mutate(IAggregateEvent @event) { this.mutator.Mutate(this, @event); this.Version++; }
internal void UpdateEventDetails(IAggregateEvent @event, IAggregate aggregate) { var handler = eventHandlers.First(h => h.CanHandleEvent(@event)); handler.UpdateEventDetails(@event, aggregate, this); }
public UncommittedEvent(IAggregateEvent aggregateEvent, IMetadata metadata) { AggregateEvent = aggregateEvent; Metadata = metadata; }
public Task On(IAggregateEvent<Sample.CounterReset> ev) { this.onMsg(ev); return Task.FromResult(true); }