-
New: While EventFlow tries to limit the about of painful API changes, the introduction of execution/command results are considered a necessary step towards as better API.
Commands and command handlers have been updated to support execution results. Execution results is meant to be an alternative to throwing domain exceptions to do application flow. In short, before you were required to throw an exception if you wanted to abort execution and "return" a failure message.
The introduction of execution results changes this, as it allows returning a failed result that is passed all the way back to the command publisher. Execution results are generic and can thus contain e.g. any validation results that a UI might need. The
ICommandBus.PublishAsyncsignature has changed to reflect this.from
Task<ISourceId> PublishAsync<TAggregate, TIdentity, TSourceIdentity>( ICommand<TAggregate, TIdentity, TSourceIdentity> command) where TAggregate : IAggregateRoot<TIdentity> where TIdentity : IIdentity where TSourceIdentity : ISourceId
to
Task<TExecutionResult> PublishAsync<TAggregate, TIdentity, TExecutionResult>( ICommand<TAggregate, TIdentity, TExecutionResult> command, CancellationToken cancellationToken) where TAggregate : IAggregateRoot<TIdentity> where TIdentity : IIdentity where TExecutionResult : IExecutionResult
Command handler signature has changed from
Task ExecuteAsync( TAggregate aggregate, TCommand command, CancellationToken cancellationToken);
to
Task<TExecutionResult> ExecuteCommandAsync( TAggregate aggregate, TCommand command, CancellationToken cancellationToken)
Migrating to the new structure should be seamless if your current code base inherits its command handlers from the provided
CommandHandler<,,>base class. -
Breaking: Source IDs on commands have been reworked to "make room" for execution results on commands. The generic parameter from
ICommand<,,>andICommandHandler<,,,>has been removed in favor of the new execution results.ICommand.SourceIdis now of typeISourceIdinstead of using the generic type and theICommandBus.PublishAsyncno longer returnsTask<ISourceId>To get code that behaves similar to the previous version, simply take the
ISourceIdfrom the command, i.e., instead of thisvar sourceId = await commandBus.PublishAsync(command);
write this
await commandBus.PublishAsync(command); var sourceId = command.SourceId;
(
CancellationTokenand.ConfigureAwait(false)omitted fromt he above) -
Breaking: Upgraded NuGet dependency on
RabbitMQ.Clientfrom>= 4.1.3to>= 5.0.1
- Breaking: Upgraded
EventStore.Clientdependency to version 4.0 - Breaking: Changed target framework for
EventFlow.EventStores.EventStoreto .NET 4.6.2 as required byEventStore.ClientNuGet dependency - Fix:
EventFlow.Hangfirenow depends onHangfire.Coreinstead ofHangfire - New: Added an overload to
IDomainEventPublisher.PublishAsyncthat isn't generic and doesn't require an aggregate ID - New: Added
IReadModelPopulator.DeleteAsyncthat allows deletion of single read models - Obsolete:
IDomainEventPublisher.PublishAsync<,>(generic) in favor of the new less restrictive non-generic overload
- Breaking: Moved non-async methods on
IReadModelPopulatorto extension methods - New: Added non-generic overloads for purge and populate methods on
IReadModelPopulator - New: Provided
EventFlow.TestHelperswhich contains several test suites that is useful when developing event and read model stores for EventFlow. The package is an initial release and its interface is unstable and subject to change - New: Now possible to configure retry delay for MSSQL error
40501(server too busy) usingIMsSqlConfiguration.SetServerBusyRetryDelay(RetryDelay) - New: Now possible to configure the retry count of transient exceptions for
MSSQL and SQLite using the
ISqlConfiguration.SetTransientRetryCount(int) - Fixed: Added MSSQL error codes
10928,10929,18401and40540as well as a few nativeWin32Exceptionexceptions to the list treated as transient errors, i.e., EventFlow will automatically retry if the server returns one of these
- New: To be more explicit,
IEventFlowOpions.AddSynchronousSubscriber<,,,>andIEventFlowOpions.AddAsynchronousSubscriber<,,,>generic methods - Fix:
IEventFlowOpions.AddSubscriber,IEventFlowOpions.AddSubscribersand
IEventFlowOpions.AddDefaultsnow correctly registers implementations ofISubscribeAsynchronousTo<,,> - Obsolete:
IEventFlowOpions.AddSubscriberis marked obsolete in favor of its explicite counterparts
- Fix: EventFlow now uses a Autofac lifetime scope for validating service
registrations when
IEventFlowOpions.CreateResolver(true)is invoked. Previously services were created but never disposed as they were resolved using the root container
- Breaking: Asynchronous subscribers are now disabled by default, i.e.,
any implementations of
ISubscribeAsynchronousTo<,,>wont get invoked unless enabledtheeventFlowOptions.Configure(c => IsAsynchronousSubscribersEnabled = true);ITaskRunnerhas been removed and asynchronous subscribers are now invoked using a new scheduled job that's scheduled to run right after the domain events are emitted. Using theITaskRunnerled to unexpected task terminations, especially if EventFlow was hosted in IIS. If enabling asynchronous subscribers, please make sure to configure proper job scheduling, e.g. by using theEventFlow.HangfireNuGet package. The default job scheduler isInstantJobScheduler, which executes jobs synchronously, giving a end result similar to that of synchronous subscribers - Breaking:
InstantJobScheduler, the default in-memory scheduler if nothing is configured, now swallows all job exceptions and logs them as errors. This ensure that theInstantJobSchedulerbehaves as any other out-of-process job scheduler
- New: .NET Standard 1.6 support for the following NuGet packages
EventFlowEventFlow.AutofacEventFlow.ElasticsearchEventFlow.HangfireEventFlow.RabbitMQ
- Fixed: Removed dependency
Microsoft.Owin.Host.HttpListenerfromEventFlow.Owinas it doesn't need it
- Breaking: Updated all NuGet package dependencies to their latest versions
- New: EventFlow now embeds PDB and source code within the assemblies using SourceLink (GitLink now removed)
- Fixed: The deterministic
IDomainEvent.Metadata.EventIdis now correctly based on the both the aggregate identity and the aggregate sequence number, instead of merely the aggregate identity - Fixed: GitLink PDB source URLs
- New: NuGet packages now contain PDB files with links to GitHub
(thanks to GitLink). Be sure
to check
Enable source server supportto be able to step through the EventFlow source code. See GitLink documentation for details - Fixed: Fixed a bug in how EventFlow registers singletons with Autofac
that made Autofac invoke
IDisposable.Dispose()upon disposing lifetime scopes
- New: Updated EventFlow logo (thanks @olholm)
- Fixed: Corrected logo path in NuGet packages
- New: Autofac is no longer IL merged into the
EventFlowcore NuGet package. This is both in preparation for .NET Core and to simplify the build process. EventFlow now ships with a custom IoC container by default. The Autofac based IoC container is still available via theEventFlow.Autofacand will continue to be supported as it is recommended for production use - New: An IoC container based aggregate root factory is now the default aggregate factory. The old implementation merely invoked a constructor with the aggregate ID as argument. The new default also checks if any additional services are required for the constructor making the distinction between the two obsolete
- New:
Command<,,>now inherits fromValueObject - Obsolete:
UseResolverAggregateRootFactory()andUseAutofacAggregateRootFactory()are marked as obsolete as this is now the default. The current implementation of these methods does nothing - Obsolete: All
IEventFlowOptions.AddAggregateRoots(...)overloads are obsolete, the aggregate factory no longer has any need for the aggregate types to be registered with the container. The current implementation of the method does nothing
- Fix: Single aggregate read models can now be re-populated again
- Breaking: Remove the following empty and deprecated MSSQL NuGet packages. If
you use any of these packages, then switch to the
EventFlow.MsSqlpackageEventFlow.EventStores.MsSqlEventFlow.ReadStores.MsSql
- Breaking:
ITaskRunner.Run(...)has changed signature. The task factory now gets an instance ofIResolverthat is valid for the duration of the task execution - Fixed: The resolver scope of
ISubscribeAsynchronousTo<,,>is now valid for the duration of the domain handling - New: Documentation is now released in HTML format along with NuGet packages. Access the ZIP file from the GitHub releases page
- New: Documentation is now hosted at http://docs.geteventflow.net/ and http://eventflow.readthedocs.io/ and while documentation is still kept along the source code, the documentation files have been converted from markdown to reStructuredText
- New: Added
ISubscribeAsynchronousTo<,,>as an alternative to the existingISubscribeSynchronousTo<,,>, which allow domain event subscribers to be executed using the newITaskRunner - New: Added
ITaskRunnerfor which the default implementation is mere a thin wrapper aroundTask.Run(...)with some logging added. Implemting this interface allows control of how EventFlows runs tasks. Please note that EventFlow will only useITaskRunnerin very limited cases, e.g. if there's implantations ofISubscribeAsynchronousTo<,,>
- Fixed:
IAggregateStore.UpdateAsyncandStoreAsyncnow publishes committed events as expected. This basically means that its now possible to circumvent the command and command handler pattern and use theIAggregateStore.UpdateAsyncdirectly to modify an aggregate root - Fixed: Domain events emitted from aggregate sagas are now published
- New core feature: EventFlow now support sagas, also known as process managers. The use of sagas is opt-in. Currently EventFlow only supports sagas based on aggregate roots, but its possible to implement a custom saga store. Consult the documentation for details on how to get started using sagas
- New: Added
IMemoryCachefor which the default implementation is a thin wrapper for the .NET built-inMemoryCache. EventFlow relies on extensive use of reflection and the internal parts of EventFlow will move to this implementation for caching internal reflection results to allow better control of EventFlow memory usage. Invoke theUsePermanentMemoryCache()extension method onIEventFlowOptionsto have EventFlow use the previous cache behavior usingConcurrentDictionary<,,>based in-memory cache - New: Added
Identity<>.With(Guid)which allows identities to be created based on a specificGuid - New: Added
Identity<>.GetGuid()which returns the internalGuid
- Fixed: Fixed regression in
v0.32.2163by adding NuGet package referenceDbUptoEventFlow.Sql. The package was previously ILMerged. - Fixed: Correct NuGet package project URL
- Breaking: This release contains several breaking changes related to
Elasticsearch read models
- Elasticsearch NuGet package has been renamed to
EventFlow.Elasticsearch - Upgraded Elasticsearch dependencies to version 2.3.3
- Purging all read models from Elasticsearch for a specific type now deletes the index instead of doing a delete by query. Make sure to create a separate index for each read model. Delete by query has been moved to a plugin in Elasticsearch 2.x and deleting the entire index is now recommended
- The default index for a read model is now
eventflow-[lower case type name], e.g.eventflow-thingyreadmodel, instead of merelyeventflow
- Elasticsearch NuGet package has been renamed to
- Breaking: The following NuGet dependencies have been updated
Elasticsearch.Netv2.3.3 (up from v1.7.1)Elasticsearch.Net.JsonNETremovedNESTv2.3.3 (up from v1.7.1)Newtonsoft.Jsonv8.0.3 (up from v7.0.1)
- Breaking: Several non-async methods have been moved from the following
interfaces to extension methods and a few additional overloads have
been created
IEventStoreICommandBus
- New: EventFlow can now be configured to throw exceptions thrown by subscribers
by
options.Configure(c => c.ThrowSubscriberExceptions = true) - New: Added an
ICommandSchedulerfor easy scheduling of commands
- Breaking: To simplify the EventFlow NuGet package structure, the two NuGet
packages
EventFlow.EventStores.MsSqlandEventFlow.ReadStores.MsSqlhave been discontinued and their functionality move to the existing packageEventFlow.MsSql. The embedded SQL scripts have been made idempotent making the upgrade a simple operation of merely using the new name spaces. To make the upgrade easier, the deprecated NuGet packages will still be uploaded, but will not contain anything - Fixed: When configuring Elasticsearch and using the overload of
ConfigureElasticsearchthat takes multiple of URLs,SniffingConnectionPoolis now used instead ofStaticConnectionPooland with sniff life span of five minutes
- Breaking:
IAggregateRoothas some breaking changes. If these methods aren't used (which is considered the typical case), then the base classAggregateRoot<,,>will automatically handle itCommitAsynchas an additionalISnapshotStoreparameter. If you don't use snapshot aggregates, then you can safely passnullLoadAsyncis a new method that lets the aggregate control how its loaded fromt the event- and snapshot stores
- New core feature: EventFlow now support snapshot creation for aggregate
roots. The EventFlow documentation has been updated to include a guide on
how to get started using snapshots. Snapshots are basically an opt-in optimized
method for handling long-lived aggregate roots. Snapshot support in EventFlow
introduces several new elements, read the documentation to get an overview.
Currently EventFlow offers the following snapshot stores
- In-memory
- Microsoft SQL Server
- New: The
IAggregateStoreis introduced, which provides a cleaner interface for manipulating aggregate roots. The most important method is theUpdateAsyncwhich allows easy updates to aggregate roots without the need for a command and command handlerLoadAsyncUpdateAsyncStoreAsync
- New:
IEventStorenow supports loading events from a specific version using the new overload ofLoadEventsAsyncthat takes afromEventSequenceNumberargument - New:
IMsSqlDatabaseMigratornow has a overloaded method namedMigrateDatabaseUsingScriptsthat takes anIEnumerable<SqlScript>enabling specific scripts to be used in a database migration - New: Added suport to use EventStore persistence with connection strings instead IPs only
- Obsolete: The following aggregate related methods on
IEventStorehas been marked as obsolete in favor of the newIAggregateStore. The methods will be removed at some point in the futureLoadAggregateAsyncLoadAggregate
- Critical fix:
OptimisticConcurrencyRetryStrategynow correctly only states thatOptimisticConcurrencyExceptionshould be retried. Before ALL exceptions from the event stores were retried, not only the transient! If you have inadvertently become dependent on this bug, then implement your ownIOptimisticConcurrencyRetryStrategythat has the old behavior - Fixed:
OptimisticConcurrencyRetryStrategyhas a off-by-one error that caused it to retry one less that it actually should - Fixed: Prevent
abstract ICommandHandler<,,,>from being registered inEventFlowOptionsCommandHandlerExtensions.AddCommandHandlers(...) - Fixed: Prevent
abstract IEventUpgrader<,>from being registered inEventFlowOptionsEventUpgradersExtensions.AddEventUpgraders(...) - Fixed: Prevent
abstract IMetadataProviderfrom being registered inEventFlowOptionsMetadataProvidersExtensions.AddMetadataProviders(...) - Fixed: Prevent
abstract IQueryHandler<,>from being registered inEventFlowOptionsQueriesExtensions.AddQueryHandlers(...) - Fixed: Prevent
abstract ISubscribeSynchronousTo<,,>from being registered inEventFlowOptionsSubscriberExtensions.AddSubscribers(...)
- New: Configure Hangfire job display names by implementing
IJobDisplayNameBuilder. The default implementation uses job description name and version
- Breaking: Renamed
MssqlMigrationExceptiontoSqlMigrationException - Breaking: Renamed
SqlErrorRetryStrategytoMsSqlErrorRetryStrategyas its MSSQL specific - Breaking: The NuGet package
Dapperis no longer IL merged with the packageEventFlow.MsSqlbut is now listed as a NuGet dependency. The current version used by EventFlow isv1.42 - New: Introduced the NuGet package
EventFlow.SQLitethat adds event store support for SQLite databases, both as event store and read model store - New: Introduced the NuGet package
EventFlow.Sqlas shared package for EventFlow packages that uses SQL - New: Its now possible to configure the retry delay for MSSQL transient
errors using the new
IMsSqlConfiguration.SetTransientRetryDelay. The default is a random delay between 50 and 100 milliseconds
-
Fixed: Deadlock in
AsyncHelperif e.g. an exception caused noasynctasks to be scheduled. TheAsyncHelperis used by EventFlow to expose non-asyncmethods to developers and provide the means to callasyncmethods from a synchronous context without causing a deadlock. There's no change to any of theasyncmethods.The
AsyncHelperis used in the following methods.ICommandBus.PublishIEventStore.LoadEventsIEventStore.LoadAggregateIEventStore.LoadAllEventsIJobRunner.ExecuteIReadModelPopulator.PopulateIReadModelPopulator.PurgeIQueryProcessor.Process
- Breaking: The following NuGet references have been updated
EventStore.Clientv3.4.0 (up from v3.0.2)Hangfire.Corev1.5.3 (up from v1.4.6)RabbitMQ.Clientv3.6.0 (up from v3.5.4)
- New: EventFlow now uses Paket to manage NuGet packages
- Fixed: Incorrect use of
EventStore.Clientthat caused it to throwWrongExpectedVersionExceptionwhen committing aggregates multiple times - Fixed: Updated NuGet package titles of the following NuGet packages to
contain assembly name to get a better overview when searching on
nuget.org
EventFlow.RabbitMQEventFlow.EventStores.EventStore
- Fixed: Updated internal NuGet reference
dbupto v3.3.0 (up from v3.2.1)
- Breaking: EventFlow no longer ignores columns named
Idin MSSQL read models. If you were dependent on this, use theMsSqlReadModelIgnoreColumnattribute - Fixed: Instead of using
MethodInfo.Invoketo call methods on reflected types, e.g. when a command is published, EventFlow now compiles an expression tree instead. This has a slight initial overhead, but provides a significant performance improvement for subsequent calls - Fixed: Read model stores are only invoked if there's any read model updates
- Fixed: EventFlow now correctly throws an
ArgumentExceptionif EventFlow has been incorrectly configure with known versioned types, e.g. an event is emitted that hasn't been added during EventFlow initialization. EventFlow would handle the save operation correctly, but if EventFlow was reinitialized and the event was loaded before it being emitted again, an exception would be thrown as EventFlow would know which type to use. Please make sure to correctly load all event, command and job types before use - Fixed:
IReadModelFactory<>.CreateAsync(...)is now correctly used in read store mangers - Fixed: Versioned type naming convention now allows numbers
- New: To customize how a specific read model is initially created, implement
a specific
IReadModelFactory<>that can bootstrap that read model - New: How EventFlow handles MSSQL read models has been refactored to allow
significantly more freedom to developers. MSSQL read models are no longer
required to implement
IMssqlReadModel, only the emptyIReadModelinterface. Effectively, this means that no specific columns are required, meaning that the following columns are no longer enforced on MSSQL read models. Use the new requiredMsSqlReadModelIdentityColumnattribute to mark the identity column and the optional (but recommended)MsSqlReadModelVersionColumnto mark the version column.string AggregateIdDateTimeOffset CreateTimeDateTimeOffset UpdatedTimeint LastAggregateSequenceNumber
- Obsolete:
IMssqlReadModelandMssqlReadModel. Developers should instead use theMsSqlReadModelIdentityColumnandMsSqlReadModelVersionColumnattributes to mark the identity and version columns (read above). EventFlow will continue to supportIMssqlReadModel, but it will be removed at some point in the future - Fixed: Added missing
UseElasticsearchReadModel<TReadModel, TReadModelLocator>()extension
- New: Added
Identity<>.NewComb()that creates sequential unique IDs which can be used to minimize database fragmentation - New: Added
IReadModelContext.Resolverto allow read models to fetch additional resources when events are applied - New: The
PrettyPrint()type extension method, mostly used for verbose logging, now prints even prettier type names, e.g.KeyValuePair<Boolean,Int64>instead of merelyKeyValuePair'2, making log messages slightly more readable
- Breaking:
Entity<T>now inherits fromValueObjectbut uses only theIdfield as equality component. OverrideGetEqualityComponents()if you have a different notion of equality for a specific entity - Breaking:
Entity<T>will now throw anArgumentNullExceptionif theidpassed to its constructor isnull - Breaking: Fixed method spelling. Renamed
ISpecification<T>.WhyIsNotStatisfiedBytoWhyIsNotSatisfiedByandSpecification<T>.IsNotStatisfiedBecausetoIsNotSatisfiedBecause - New: Read model support for Elasticsearch via the new NuGet package
EventFlow.ReadStores.Elasticsearch
- Breaking:
AddDefaultsnow also adds the job type definition to theIJobsDefinitonService - New: Implemented a basic specification pattern by providing
ISpecification<T>, an easy-to-useSpecificaion<T>and a set of extension methods. Look at the EventFlow specification tests to get started - Fixed:
IEventDefinitionService,ICommandDefinitonServiceandIJobsDefinitonServicenow longer throw an exception if an existing event is loaded, i.e., multiple calls toAddEvents(...),AddCommand(...)andAddJobs(...)no longer throws an exception - Fixed:
DomainError.With(...)no longer executesstring.formatif only one argument is parsed
- POTENTIAL DATA LOSS for the files event store: The EventFlow
internal functionality regarding event stores has been refactored resulting
in information regarding aggregate names being removed from the event
persistence layer. The files based event store no longer stores its events in
the path
[STORE PATH]\[AGGREGATE NAME]\[AGGREGATE ID]\[SEQUENCE].json, but in the path[STORE PATH]\[AGGREGATE ID]\[SEQUENCE].json. Thus if you are using the files event store for tests, you should move the events into the new file structure. Alternatively, implement the newIFilesEventLocatorand provide your own custom event file layout. - Breaking: Event stores have been split into two parts, the
IEventStoreand the newIEventPersistence.IEventStorehas the same interface before but the implementation is now no longer responsible for persisting the events, only converting and serializing the persisted events.IEventPersistencehandles the actual storing of events and thus if any custom event stores have been implemented, they should implement to the newIEventPersistenceinstead. - New: Added
IEntity,IEntity<>and an optionalEntity<>that developers can use to implement DDD entities.
- Fixed: Using NuGet package
EventFlow.Autofaccauses an exception with the messageThe type 'EventFlow.Configuration.Registrations.AutofacStartable' is not assignable to service 'Autofac.IStartableduring EventFlow setup
- Breaking: Removed
HasRegistrationFor<>andGetRegisteredServices()fromIServiceRegistrationand added them toIResolverinstead. The methods required that all service registrations went through EventFlow, which in most cases they will not - Obsolete: Marked
IServiceRegistration.RegisterIfNotRegistered(...), use thekeepDefault = trueon the otherRegister(...)methods instead - New: Major changes have been done to how EventFlow handles service
registration and bootstrapping in order for developers to skip calling
CreateResolver()(orCreateContainer()if using theEventFlow.Autofacpackage) completely. EventFlow will register its bootstrap services in the IoC container and configure itself whenever the container is created - New: Introduced
IBootstrapinterface that you can register. It has a singleBootAsync(...)method that will be called as soon as the IoC container is ready (similar to that ofIStartableof Autofac) - Fixed: Correct order of service registration decorators. They are now applied in the same order they are applied, e.g., the last registered service decorator will be the "outer" service
- Fixed: Added missing
ICommand<,>interface to abstractCommand<,>class inEventFlow.Commands.
- Fixed: Added
UseHangfireJobScheduler()and markedUseHandfireJobScheduler()obsolete, fixing method spelling mistake
- Breaking: All
EventFlowOptionsextensions are nowIEventFlowOptionsinstead andEventFlowOptionsimplements this interface. If you have made your own extensions, you will need to use the newly created interface instead. Changed in order to make testing of extensions and classes dependent on the EventFlow options easier to test - New: You can now bundle your configuration of EventFlow into modules that
implement
IModuleand register these by callingEventFlowOptions.RegisterModule(...) - New: EventFlow now supports scheduled job execution via e.g. Hangfire. You
can create your own scheduler or install the new
EventFlow.HangfireNuGet package. Read the jobs documentation for more details - New: Created the OWIN
CommandPublishMiddlewaremiddleware that can handle publishing of commands by posting a JSON serialized command to e.g./commands/ping/1in whichpingis the command name and1its version. Remember to add authentication - New: Created a new interface
ICommand<TAggregate,TIdentity,TSourceIdentity>to allow developers to control the type ofICommand.SourceId. Using theICommand<TAggregate,TIdentity>(or Command<TAggregate,TIdentity>) will still yield the same result as before, i.e.,ICommand.SourceIdbeing of typeISourceId - New: The
AddDefaults(...)now also adds the command type definition to the newICommandDefinitonService
- Breaking:
EventFlowOptions.AddDefaults(...)now also adds query handlers - New: Added an optional
Predicate<Type>to the following option extension methods that scan anAssembly:AddAggregateRoots(...),AddCommandHandlers(...),AddDefaults(...),AddEventUpgraders(...),AddEvents(...),AddMetadataProviders(...),AddQueryHandlers(...)andAddSubscribers(...) - Fixed:
EventFlowOptions.AddAggregateRoots(...)now prevents abstract classes from being registered when passingIEnumerable<Type> - Fixed: Events published to RabbitMQ are now in the right order for chains
of subscribers, if
event A -> subscriber -> command -> aggregate -> event B, then the order of published events to RabbitMQ wasevent Band thenevent A
- Breaking: Aggregate root no longer have
Aggregateremoved from their when name, i.e., the metadata property with keyaggregate_name(orMetadataKeys.AggregateName). If you are dependent on the previous naming, use the newAggregateNameattribute and apply it to your aggregates - Breaking: Moved
Identity<>andIIdentityfrom theEventFlow.Aggregatesnamespace toEventFlow.Coreas the identities are not specific for aggregates - Breaking:
ICommand.Idis renamed toICommand.AggregateIdto make "room" for the newICommand.SourceIdproperty. If commands are serialized, then it might be important verify that the serialization still works. EventFlow does not serialize commands, so no mitigation is provided. If theCommand<,>is used, make sure to use the correct protected constructor - Breaking:
IEventStore.StoreAsync(...)now requires an additionalISourceIdargument. To create a random one, useSourceId.New, but it should be e.g. the command ID that resulted in the events. Note, this method isn't typically used by developers - New: Added
ICommand.SourceId, which contains the ID of the source. The default (if your commands inherit fromCommand<,>) will be a newCommandIdeach time the aCommand<,>instance is created. You can pass specific value, merely use the newly added constructor taking the ID. Alternatively you commands could inherit from the newDistinctCommand, enabling commands with the same state to have the sameSourceId - New: Duplicate commands can be detected using the new
ISourceId. Read the EventFlow article regarding commands for more details - New: Aggregate names can now be configured using the attribute
AggregateName. The name can be accessed using the newIAggregateRoot.Nameproperty - New: Added
Identity<>.NewDeterministic(Guid, string)enabling creation of deterministic GUIDs - New: Added new metadata key
source_id(MetadataKeys.SourceId) containing the source ID, typically the ID of the command from which the event originated - New: Added new metadata key
event_id(MetadataKeys.EventId) containing a deterministic ID for the event. Events with the same aggregate sequence number and from aggregates with the same identity, will have the same event identity - Fixed:
Identity<>.With(string)now throws anArgumentExceptioninstead of aTargetInvocationExceptionwhen passed an invalid identity - Fixed: Aggregate roots now build the cache of
Applymethods once, instead of when the method is requested the first time
- Breaking:
EventFlowOptions.AddDefaults(...)now also adds event definitions - New: RabbitMQ is now supported through the new
NuGet package called
EventFlow.RabbitMQwhich enables domain events to be published to the bus - New: If you want to subscribe to all domain events, you can implement
and register a service that implements
ISubscribeSynchronousToAll. Services that implement this will automatically be added using theAddSubscribers(...)orAddDefaults(...)extension toEventFlowOptions - New: Use
EventFlowOptions.UseAutofacAggregateRootFactory(...)to use an Autofac aggregate root factory, enabling you to use services in your aggregate root constructor - New: Use
EventFlowOptions.UseResolverAggregateRootFactory()to use the resolver to create aggregate roots. Same asUseAutofacAggregateRootFactory(...)but for when using the internal IoC container - New: Use
EventFlowOptions.AddAggregateRoots(...)to register aggregate root types - New: Use
IServiceRegistration.RegisterType(...)to register services by type
- Breaking: Updated NuGet reference
Newtonsoft.Jsonto v7.0.1 (up from v6.0.8) - Breaking: Remove the empty constructor from
SingleValueObject<> - New: Added
SingleValueObjectConverterto help create clean JSON when e.g. domain events are serialized - New: Added a protected method
Register(IEventApplier)toAggregateRoot<,>that enables developers to override how events are applied. Use this to e.g. implement state objects - New: Create
AggregateState<,,>that developers can use to create aggregate state objects. CallRegister(...)with the state object as argument to redirect events to it - New: Allow
AggregateRoot<,>.Apply(...), i.e., methods for applying events, to beprivateandprotected - New: Made
AggregateRoot<,>.Emit(...)protected and virtual to allow overrides that e.g. add a standard set of metadata from the aggregate state. - New: Made
AggregateRoot<,>.ApplyEvent(...)protected and virtual to allow more custom implementations of applying events to the aggregate root. - Fixed: Updated internal NuGet reference
Dapperto v1.42 (up from v1.38)
- Braking:
IEventStore.LoadAllEventsAsyncandIEventStore.LoadAllEventsnow take aGlobalPositionas an argument instead of alongfor the starting position. TheGlobalPositionis basically a wrapper around a string that hides the inner workings of each event store. - New: NuGet package
EventFlow.EventStores.EventStorethat provides integration to Event Store. Its an initial version and shouldn't be used in production.
-
Breaking: Remove all functionality related to global sequence numbers as it proved problematic to maintain. It also matches this quote:
Order is only assured per a handler within an aggregate root boundary. There is no assurance of order between handlers or between aggregates. Trying to provide those things leads to the dark side.
Greg Young
- If you use a MSSQL read store, be sure to delete the
LastGlobalSequenceNumbercolumn during update, or set it to defaultNULL IDomainEvent.GlobalSequenceNumberremovedIEventStore.LoadEventsAsyncandIEventStore.LoadEventstaking aGlobalSequenceNumberRangeremoved
- If you use a MSSQL read store, be sure to delete the
-
Breaking: Remove the concept of event caches. If you really need this then implement it by registering a decorator for
IEventStore -
Breaking: Moved
IDomainEvent.BatchIdto metadata and createdMetadataKeys.BatchIdto help access it -
New:
IEventStore.DeleteAggregateAsyncto delete an entire aggregate stream. Please consider carefully if you really want to use it. Storage might be cheaper than the historic knowledge within your events -
New:
IReadModelPopulatoris new and enables you to both purge and populate read models by going though the entire event store. Currently its only basic functionality, but more will be added -
New:
IEventStorenow hasLoadAllEventsAsyncandLoadAllEventsthat enables you to load all events in the event store a few at a time. -
New:
IMetadata.TimestampEpochcontains the Unix timestamp version ofIMetadata.Timestamp. Also, an additional metadata keytimestamp_epochis added to events containing the same data. Note, theTimestampEpochonIMetadatahandles cases in which thetimestamp_epochis not present by using the existing timestamp -
Fixed:
AggregateRoot<>now reads the aggregate version from domain events applied during aggregate load. This resolves an issue for when anIEventUpgraderremoved events from the event stream -
Fixed:
InMemoryReadModelStore<,>is now thread safe
- New: EventFlow now includes a
IQueryProcessorthat enables you to implement queries and query handlers in a structure manner. EventFlow ships with two ready-to-use queries and related handlersReadModelByIdQuery<TReadModel>: Supported by in-memory and MSSQL read model storesInMemoryQuery<TReadModel>: Only supported by in-memory read model store, but lets you search for any read model based on aPredicate<TReadModel>
- Breaking: Read models have been significantly improved as they can now
subscribe to events from multiple aggregates. Use a custom
IReadModelLocatorto define how read models are located. The suppliedILocateByAggregateIdsimply uses the aggregate ID. To subscribe to other events, simply implementIAmReadModelFor<,,>and make sure you have supplied a proper read model locator.UseMssqlReadModelsignature changed, change to.UseMssqlReadModel<MyReadModel, ILocateByAggregateId>()in order to have the previous functionalityUseInMemoryReadStoreForsignature changed, change to.UseInMemoryReadStoreFor<MyReadModel, ILocateByAggregateId>()in order to have the previous functionality
- Breaking: A warning is no longer logged if you forgot to subscribe to a aggregate event in your read model as read models are no longer strongly coupled to a specific aggregate and its events
- Breaking:
ITransientFaultHandlernow takes the strategy as a generic argument instead of theUse<>method. If you want to configure the retry strategy, useConfigureRetryStrategy(...)instead - New: You can now have multiple
IReadStoreManagerif you would like to implement your own read model handling - New:
IEventStorenow has aLoadEventsAsyncandLoadEventsthat loadsIDomainEvents based on global sequence number range - New: Its now possible to register generic services without them being
constructed generic types, i.e., register
typeof(IMyService<>)astypeof(MyService<>) - New: Table names for MSSQL read models can be assigned using the
TableAttributefromSystem.ComponentModel.DataAnnotations - Fixed: Subscribers are invoked after read stores have been updated, which ensures that subscribers can use any read models that were updated
- POTENTIAL DATA LOSS for files event store: Files event store now
stores its log as JSON instead of an
intin the form{"GlobalSequenceNumber":2}. So rename the current file and put in the global sequence number before startup - Breaking: Major changes has been made regarding how the aggregate
identity is implemented and referenced through interfaces. These changes makes
it possible to access the identity type directly though all interface. Some
notable examples are listed here. Note that this has NO impact on how data
is stored!
IAggregateRootchanged toIAggregateRoot<TIdentity>ICommand<TAggregate>changed toICommand<TAggregate,TIdentity>ICommandHandler<TAggregate,TCommand>changed toICommandHandler<TAggregate,TIdentity, TCommand>IAmReadModelFor<TEvent>changed toIAmReadModelFor<TAggregate,TIdentity,TEvent>IDomainEvent<TEvent>changed toIDomainEvent<TAggregate,TIdentity>
- New:
ICommandBus.Publishnow takes aCancellationTokenargument - Fixed: MSSQL should list columns to SELECT when fetching events
- Breaking:
ValueObjectnow uses public properties instead of both private and public fields - Breaking: Aggregate IDs are no longer
stringbut objects implementingIIdentity - Breaking: MSSQL transient exceptions are now retried
- Breaking: All methods on
IMsSqlConnectionhas an extraLabelargument - New:
ITransientFaultHandleradded along with default retry strategies for optimistic concurrency and MSSQL transient exceptions - New: Release notes added to NuGet packages
- New: Better logging and more descriptive exceptions
- Fixed: Unchecked missing in
ValueObjectwhen claculating hash - Fixed:
NullReferenceExceptionthrown ifnullwas stored inSingleValueObjectandToString()was called
- First stable version of EventFlow