private IEventStream PrepareStream(IAggregate aggregate, Dictionary <string, object> headers) { IEventStream stream; if (!this.streams.TryGetValue(aggregate.Id, out stream)) { this.streams[aggregate.Id] = stream = this.eventStore.CreateStream(aggregate.Id); } foreach (var item in headers) { stream.UncommittedHeaders[item.Key] = item.Value; } aggregate.GetUncommittedEvents() .Cast <object>() .Select(x => new EventMessage { Body = x }) .ToList() .ForEach(stream.Add); aggregate.ClearUncommittedEvents(); return(stream); }
public void Save(IAggregate aggregate) { var commitId = Guid.NewGuid(); var events = aggregate.GetUncommittedEvents().ToArray(); if (events.Any() == false) { return; } var streamName = GetStreamName(aggregate.GetType(), aggregate.Id); var originalVersion = aggregate.Version - events.Count(); var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var commitHeaders = new Dictionary <string, object> { { "CommitId", commitId }, { "AggregateClrType", aggregate.GetType().AssemblyQualifiedName } }; var eventsToSave = events.Select(e => e.ToEventData(commitHeaders)).ToList(); _connection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait(); aggregate.ClearUncommittedEvents(); foreach (var evento in events) { if (evento.GetType().ToString().Equals("CantidadIncrementada")) { new HandlerCantidadIncrementada().Hanlder((CantidadIncrementada)evento); } else if (evento.GetType().ToString().Equals("CantidadDecrementada")) { new HandlerCantidadDecrementada().Hanlder((CantidadDecrementada)evento); } } }
public void Save(string bucketId, IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { Dictionary <string, object> headers = PrepareHeaders(aggregate, updateHeaders); while (true) { IEventStream stream = PrepareStream(bucketId, aggregate, headers); int commitEventCount = stream.CommittedEvents.Count; try { stream.CommitChanges(commitId); aggregate.ClearUncommittedEvents(); return; } catch (DuplicateCommitException) { stream.ClearChanges(); return; } catch (ConcurrencyException e) { var conflict = ThrowOnConflict(stream, commitEventCount); stream.ClearChanges(); if (conflict) { throw new ConflictingCommandException(e.Message, e); } } catch (StorageException e) { throw new PersistenceException(e.Message, e); } } }
public void Store(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commit = new Commit { CommitId = commitId, AggregateId = aggregate.Id, CommitStamp = DateTimeOffset.UtcNow }; using (var session = string.IsNullOrWhiteSpace(_databaseName) ? _store.OpenSession() : _store.OpenSession(_databaseName)) { session.Advanced.UseOptimisticConcurrency = true; session.Advanced.AllowNonAuthoritativeInformation = false; var stream = session.Load <EventStream>(aggregate.Id.ToStringId <EventStream>()); if (stream == null) { stream = new EventStream { Id = aggregate.Id.ToStringId <EventStream>(), AggregateType = aggregate.GetType().FullName }; session.Store(stream); } commit.CommitSequence = stream.Commits.Count + 1; commit.Events = aggregate.GetUncommittedEvents() .Cast <object>() .Select(x => new Event { Body = x, Headers = PrepareHeaders(updateHeaders) }) .ToList(); stream.Version = aggregate.Version; stream.Snapshot = aggregate; stream.Commits.Add(commitId, commit); //pre-commit hooks if (_pipelineHooks.All(hook => hook.PreCommit(commit))) { try { session.SaveChanges(); aggregate.ClearUncommittedEvents(); } catch (Exception exception) { throw; } } } //post commit hooks foreach (var hook in _pipelineHooks) { hook.PostCommit(commit, _databaseName); } }
public async Task SaveAsync(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { CommitIdHeader, commitId }, { this._aggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = this._aggregateIdToStreamName(aggregate.GetType(), aggregate.Id); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); if (eventsToSave.Count < WritePageSize) { await this._eventDataFactory.AppendToStreamAsync(aggregate, expectedVersion, eventsToSave); } else { //TODO } aggregate.ClearUncommittedEvents(); }
public virtual void Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var headers = PrepareHeaders(aggregate, updateHeaders); while (true) { var stream = this.PrepareStream(aggregate, headers); var commitEventCount = stream.CommittedEvents.Count; try { stream.CommitChanges(commitId); aggregate.ClearUncommittedEvents(); return; } catch (DuplicateCommitException) { stream.ClearChanges(); return; } catch (ConcurrencyException e) { if (this.ThrowOnConflict(stream, commitEventCount)) { throw new ConflictingCommandException(e.Message, e); } stream.ClearChanges(); } catch (StorageException e) { throw new PersistenceException(e.Message, e); } } }
public OrderedEventPayload[] Save(IAggregate aggregate) { var events = aggregate.GetUncommittedEvents().ToArray(); if (events.Any() == false) { return new OrderedEventPayload[] { } } ; // Nothing to save var aggregateType = aggregate.GetType().Name; var originalVersion = aggregate.Version - events.Count() + 1; var eventsToSave = events .Select(e => e.ToEventData(aggregateType, aggregate.Id, originalVersion++)) .ToArray(); var storedAggregateVersion = _eventRepository.GetVersionByAggregateId(aggregate.Id); if (storedAggregateVersion.HasValue && storedAggregateVersion >= originalVersion) { throw new Exception("Concurrency exception"); } var orderedEvents = _eventRepository.SaveEvents(eventsToSave); aggregate.ClearUncommittedEvents(); return(orderedEvents); }
private async Task SaveAsync(IAggregate aggregate) { var events = aggregate.GetUncomittedEvents(); foreach (var @event in events) { @event.SetAggregateId(aggregate.Id); } await domainObjectRepository.SaveAsync(aggregate, events, Guid.NewGuid()); aggregate.ClearUncommittedEvents(); }
public async void Save(IAggregate aggregate, Guid commitId, IDictionary <string, object> updateHeaders = null) { // standard data for metadata portion of persisted event var commitHeaders = new Dictionary <string, object> { // handy tracking id { CommitIdHeader, commitId }, // type of aggregate being persisted { AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; // add extra data to metadata portion of presisted event commitHeaders = (updateHeaders ?? new Dictionary <string, object>()) .Concat(commitHeaders) .GroupBy(d => d.Key) .ToDictionary(d => d.Key, d => d.First().Value); // streamname is created by func, by default agg type concat to agg id var streamName = _aggregateIdToStreamName(aggregate.GetType(), aggregate.Id); // get all uncommitted events var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); // process events so they fit the expectations of GES var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); // calculate the expected version of the agg root in event store to detirmine if concurrency conflict var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; // if numberr of events to save is small enough it can happen in one call if (eventsToSave.Count < WritePageSize) { await _eventStoreConnection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave); } // otherwise batch events and start transaction else { var transaction = await _eventStoreConnection.StartTransactionAsync(streamName, expectedVersion); var position = 0; while (position < eventsToSave.Count) { var pageEvents = eventsToSave.Skip(position).Take(WritePageSize); await transaction.WriteAsync(pageEvents); position += WritePageSize; } await transaction.CommitAsync(); } aggregate.ClearUncommittedEvents(); }
public void Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { COMMIT_ID_HEADER, commitId }, { AGGREGATE_CLR_TYPE_HEADER, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = m_AggregateIdToStreamName(aggregate.GetType(), aggregate.Id); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); var transaction = m_EventStoreConnection.StartTransaction(streamName, expectedVersion); try { if (eventsToSave.Count < WRITE_PAGE_SIZE) { transaction.Write(eventsToSave); } else { var position = 0; while (position < eventsToSave.Count) { var pageEvents = eventsToSave.Skip(position).Take(WRITE_PAGE_SIZE); transaction.Write(pageEvents); position += WRITE_PAGE_SIZE; } } //TODO: not prod code. Need to arrange serialization, data saved to ES should be same as sent to queue foreach (var @event in newEvents) { m_EventsPublisher.PublishEvent(@event); } transaction.Commit(); } catch (Exception e) { Console.WriteLine(e); //TODO:logging transaction.Rollback(); } aggregate.ClearUncommittedEvents(); }
public void Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { CommitIdHeader, commitId }, { AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = _aggregateIdToStreamName(aggregate.GetType(), aggregate.Id); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); if (eventsToSave.Count < WritePageSize) { _eventStoreConnection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait(); } else { var transaction = _eventStoreConnection.StartTransactionAsync(streamName, expectedVersion).Result; var position = 0; while (position < eventsToSave.Count) { var pageEvents = eventsToSave.Skip(position).Take(WritePageSize); transaction.WriteAsync(pageEvents).Wait(); position += WritePageSize; } transaction.CommitAsync().Wait(); } if (_outBus != null) { foreach (var evt in newEvents) { try { _outBus.Publish((Message)evt); } catch { }//TODO: see if we need to do something here } } aggregate.ClearUncommittedEvents(); }
public void Write(IAggregate aggregate, Guid commitId, Action<IDictionary<string, object>> updateHeaders) { var streamName = _aggregateIdToStreamName(aggregate.Category, aggregate.Id); var newEvents = aggregate.GetUncommittedEvents().Cast<object>().ToList(); var eventsToSave = newEvents .Select(e => e.AsJsonEvent()) .ToList(); foreach (var eventData in eventsToSave) { var data = new StreamData(streamName, eventData); FakeDatabase.Events.Add(Guid.NewGuid(), data); } aggregate.ClearUncommittedEvents(); }
public void Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { CommitIdHeader, commitId }, { AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = _aggregateIdToStreamName(aggregate.GetType(), aggregate.Id); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); if (eventsToSave.Count < WritePageSize) { _eventStoreConnection.AppendToStream(streamName, expectedVersion, eventsToSave); } else { var transaction = _eventStoreConnection.StartTransaction(streamName, expectedVersion); var position = 0; while (position < eventsToSave.Count) { var pageEvents = eventsToSave.Skip(position).Take(WritePageSize); transaction.Write(pageEvents); position += WritePageSize; } transaction.Commit(); } foreach (var newEvent in newEvents) { _bus.Publish(newEvent); } aggregate.ClearUncommittedEvents(); }
public void Should_add_event_to_uncommitted_events_and_increase_version_when_raised() { var event1 = new MyEvent(); var event2 = new MyEvent(); var sut = new DO(Guid.NewGuid(), 10); IAggregate aggregate = sut; sut.RaiseTestEvent(event1); sut.RaiseTestEvent(event2); Assert.Equal(12, sut.Version); Assert.Equal(new IEvent[] { event1, event2 }, aggregate.GetUncomittedEvents().Select(x => x.Payload).ToArray()); aggregate.ClearUncommittedEvents(); Assert.Equal(0, sut.GetUncomittedEvents().Count); }
public void Save(IAggregate aggregate) { var commitId = Guid.NewGuid(); var events = aggregate.GetUncommittedEvents().ToArray(); if (events.Any() == false) { return; } var streamName = GetTheStreamName(aggregate.GetType(), aggregate.Id); var originalVersion = aggregate.Version - events.Count(); var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var commitHeaders = new Dictionary <string, object> { { "CommitId", commitId }, { "AggregateClrType", aggregate.GetType().AssemblyQualifiedName } }; var eventsToSave = events.Select(e => e.ToEventData(commitHeaders)).ToList(); connection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait(); aggregate.ClearUncommittedEvents(); }
public async Task Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { CommitIdHeader, commitId }, { CommitDateHeader, DateTime.UtcNow }, { AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = aggregateIdToStreamName(aggregate.GetType(), aggregate.Id.Value); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); if (eventsToSave.Count < WritePageSize) { eventStoreConnection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait(); } else { var transaction = eventStoreConnection.StartTransactionAsync(streamName, expectedVersion).Result; var position = 0; while (position < eventsToSave.Count) { var pageEvents = eventsToSave.Skip(position).Take(WritePageSize); await transaction.WriteAsync(pageEvents); position += WritePageSize; } await transaction.CommitAsync(); transaction.Dispose(); } aggregate.ClearUncommittedEvents(); }
public async Task <AggregateCommit> SaveAsync(IAggregate aggregate) { if (aggregate.Id == Guid.Empty) { throw new Exception( $"The aggregate {aggregate.GetType().FullName} has tried to be saved with an empty id"); } var uncommittedEvents = aggregate.GetUncommittedEvents().Cast <IEventData>().ToArray(); var count = 0; var metadata = string.Empty; try { var col = _db.GetCollection <LocalEventTable>(); col.EnsureIndex(x => x.StreamId, false); col.InsertBulk(uncommittedEvents.Select(x => new LocalEventTable { StreamId = aggregate.Id, Version = x.Version, TransactionId = _commandContext.Transaction.Id, AppVersion = _commandContext.AppVersion, When = x.TimeStamp, Body = JsonConvert.SerializeObject(x.Event, SerializerSettings), Category = aggregate.GetType().FullName, Who = _commandContext.ImpersonatorBy?.GuidId ?? _commandContext.By.GuidId, BodyType = x.Type.FullName })); } catch (Exception e) { throw new Exception( $"The aggregate {aggregate.GetType().FullName} has tried to save events to an old version of an aggregate"); } aggregate.ClearUncommittedEvents(); return(new AggregateCommit(aggregate.Id, _commandContext.By.GuidId, metadata, uncommittedEvents)); }
public void Save(string bucketId, IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { Dictionary <string, object> headers = PrepareHeaders(aggregate, updateHeaders); while (true) { IEventStream stream = PrepareStream(bucketId, aggregate, headers); int commitEventCount = stream.CommittedEvents.Count; try { stream.CommitChanges(commitId); aggregate.ClearUncommittedEvents(); return; } catch (DuplicateCommitException) { stream.ClearChanges(); // Issue: #4 and test: when_an_aggregate_is_persisted_using_the_same_commitId_twice // should we rethtow the exception here? or provide a feedback whether the save was successful ? return; } catch (ConcurrencyException e) { var conflict = ThrowOnConflict(stream, commitEventCount); stream.ClearChanges(); if (conflict) { throw new ConflictingCommandException(e.Message, e); } } catch (StorageException e) { throw new PersistenceException(e.Message, e); } } }
private void ClearEvents(IAggregate agr) { agr.ClearUncommittedEvents(); }
/// <summary> /// Stores the aggregate changes. /// </summary> /// <param name="aggregate"></param> public void SaveChanges(IAggregate aggregate) { SaveEvents(new AggregateKey(aggregate), aggregate.Revision, aggregate.UncommittedChanges()); aggregate.ClearUncommittedEvents(); }
public void Save(IAggregate aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders) { var commitHeaders = new Dictionary <string, object> { { CommitIdHeader, commitId }, { AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName } }; updateHeaders(commitHeaders); var streamName = _aggregateIdToStreamName(aggregate.GetType(), aggregate.Id); var categoryStreamName = _aggregateTypeToCategoryStreamName(aggregate.GetType()); var newEvents = aggregate.GetUncommittedEvents().Cast <object>().ToList(); var originalVersion = aggregate.Version - newEvents.Count; var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1; var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList(); List <EventData> stream; _store.TryGetValue(streamName, out stream); List <EventData> catStream; _store.TryGetValue(categoryStreamName, out catStream); if (stream == null) { if (expectedVersion == ExpectedVersion.Any || expectedVersion == ExpectedVersion.NoStream) { stream = new List <EventData>(); _store.Add(streamName, stream); if (catStream == null) { catStream = new List <EventData>(); _store.Add(categoryStreamName, catStream); } } else { throw new WrongExpectedVersionException("Stream " + streamName + " does not exist."); } } if (stream.Count != 0 && stream.Count - 1 != expectedVersion) // a new stream will be @ version 0 { throw new AggregateException(new WrongExpectedVersionException( $"Stream {streamName} at version {stream.Count}, expected version {expectedVersion}")); } stream.AddRange(eventsToSave); catStream?.AddRange(eventsToSave); foreach (var evt in eventsToSave) { var etName = _eventNameToEventTypeStreamName(evt.Type); List <EventData> etStream; if (!_store.TryGetValue(etName, out etStream)) { etStream = new List <EventData>(); _store.Add(etName, etStream); } etStream.Add(evt); } foreach (var @event in aggregate.GetUncommittedEvents().Cast <object>().Where(@event => (@event as Message) != null)) { _bus.Publish(@event as Message); _history.Add(new Tuple <string, Message>(streamName, @event as Message)); } aggregate.ClearUncommittedEvents(); }
public static void ClearEvents(this IAggregate aggregate) { aggregate.ClearUncommittedEvents(); }
private void Hydrate(IAggregate aggregate) { GetOrAddEventList(aggregate.Id).ForEach(aggregate.ApplyEvent); aggregate.ClearUncommittedEvents(); }