Read the complete migration guide to get the full list of changes as well as recommendations on how to do the migration.
https://geteventflow.net/migrations/v0-to-v1/
(If you see any changes you feel ownership of and you want you name there, create an issue and it will get fixed asap. EventFlow would be where it is today without the grate community contributions that it have received over the years)
Changes since last 1.x pre-release, 1.0.5003-alpha
- New: .NET 8 support
- New: Enable
IEventStore
to load events to a given sequence number (thanks @SeWaS) - New:
EventFlow.Hangfire
now part of the v1 release cycle (thanks @nicolaj-hartmann) - Fix/breaking: Switch from
System.Data.SqlClient
toMicrosoft.Data.SqlClient
(thanks @janrybka)
Changes since last 1.x pre-release, 1.0.5002-alpha
- New: Read model rebuilder can be done across multiple read model types. The piping of events and applying them are now done concurrently to reduced memory usage and significantly improve time to completion (by @kyle-bradley)
- New: Created
EventFlow.Redis
(by @joshua211) - New: Migrated
EventFlow.RabbitMQ
to v1 (by @kyle-bradley) - Breaking: Removed old
EventFlow.Shims.Tasks
class that provided a wrapper forTask.CompletedTask
in frameworks that did not have it
Changes since last 1.x pre-release, 1.0.5001-alpha
- New/breaking:
IEventUpgrader<,>
are now (finally) async. For an easy upgrade experience, use the new base classEventUpgraderNonAsync
for any existing upgraders. Its aabstract
class that implements the updated interface and provides aabstract
method with the same signature as the previous interface - Fix/breaking: Event upgraders are now used during read model population. As the upgraders
are re-used across multiple aggregates, there is a high likelihood that some additions are
needed in any existing upgraders. Upgraders are stored on the new
IEventUpgradeContext
, which is created by the newIEventUpgradeContextFactory
. Replace this if you need addition context during event upgrades - Fix:
SnapshotAggregateRoot
now correctly loads previous source IDs as well adds the current source ID that triggered the snapshot. This causes theDuplicateOperationException
to be correctly thrown if a duplicate source ID as added before a snapshot was taken - Fix: Upgrade
Newtonsoft.Json
from11.0.2
to13.0.1
to fix DoS vulnerability - Fix:
UseFilesEventPersistence
should no longer throw exception for .NET regarding relative paths
Complete 1.0 change log
- New: Read model rebuilder can be done across multiple read model types. The piping of events and applying them are now done concurrently to reduced memory usage and significantly improve time to completion (by @kyle-bradley)
- New: Created
EventFlow.Redis
(by @joshua211) - New/breaking: Replace internal IoC implementation with
Microsoft.Extensions.DependencyInjection
- New/breaking: Replace internal logging implementation with
Microsoft.Extensions.Logging
- New/breaking: SQL read models now support different connection strings using the
[SqlReadModelConnectionStringName]
attribute. To allow executing queries using different connection strings, all methods onIMsSqlConnection
andISqlConnection
now have an additional argument,string connectionStringName
to signify which connection string should be used for the query - New/breaking: SQL connection strings are now fetched from the
SqlConfiguration<T>.GetConnectionStringAsync(...)
instead of a property, allowing more control of the connection string used at runtime - New/breaking:
IEventUpgrader<,>
are now (finally) async. For an easy upgrade experience, use the new base classEventUpgraderNonAsync
for any existing upgraders. Its aabstract
class that implements the updated interface and provides aabstract
method with the same signature as the previous interface - New: Its now possible to change the execution timeout for database migrations using the
SetUpgradeExecutionTimeout(...)
on the SQL configuration - Fix/breaking: Event upgraders are now used during read model population. As the upgraders
are re-used across multiple aggregates, there is a high likelihood that some additions are
needed in any existing upgraders. Upgraders are stored on the new
IEventUpgradeContext
, which is created by the newIEventUpgradeContextFactory
. Replace this if you need addition context during event upgrades - Breaking: Removed the following dead and/or confusion MSSQL attributes. The real ones
are named the same, with with
Sql...
instead ofMsSql...
MsSqlReadModelIdentityColumn
MsSqlReadModelIgnoreColumn
MsSqlReadModelVersionColumn
- Breaking: Methods on
IMsSqlDatabaseMigrator
andISqlDatabaseMigrator
have been made async and have an extraCancellationToken
argument - Breaking: Remove support for .NET Framework and consolidate on .NET (Core) LTS versions
- Breaking: Replace internal in-memory caching with
Microsoft.Extensions.Caching.Memory
- Breaking: Removed
IAmAsyncReadModelFor
and madeIAmReadModelFor
async - Breaking: Removed
EventFlow.Core.AsyncHelper
as well as all async wrapper methods that used itIAggregateStore.Load
IAggregateStore.Store
IAggregateStore.Update
ICommandBus.Publish
IEventStore.LoadAggregate
IEventStore.LoadEvents
IEventStore.LoadAllEvents
IQueryProcessor.Process
IReadModelPopulator.Populate
IReadModelPopulator.Purge
- Breaking: Removed old
EventFlow.Shims.Tasks
class that provided a wrapper forTask.CompletedTask
in frameworks that did not have it - Fix:
SnapshotAggregateRoot
now correctly loads previous source IDs as well adds the current source ID that triggered the snapshot. This causes theDuplicateOperationException
to be correctly thrown if a duplicate source ID as added before a snapshot was taken - Fix: Upgrade
Newtonsoft.Json
from11.0.2
to13.0.1
to fix DoS vulnerability - Version of 0.x included:
0.83.4713
. 0.x changes are merged to 1.x at regular intervals, but might be one or two releases behind
- New: Queue name used by HangfireJobScheduler can be overridden:
eventFlowOptions.UseHangfireJobScheduler(o => o.UseQueueName("myqueue"))
- Fixed: Do not throw
MetadataKeyNotFoundException
if there is no meta data onprevious_source_ids
in snapshots
- Fix: Allow the use of explicitly implemented interfaces in the read model
- New: added extension methods to the
EventFlow.EntityFramework
package that allow us to configure eager loading of related data. Example usage:public static IEventFlowOptions Configure(this IEventFlowOptions options) { return options .UseEntityFrameworkReadModel<MyEntity, MyDbContext>( cfg => cfg.Include(x => x.SomeProperty) .ThenInclude(y => y.SomeOtherProperty) ); }
- Fix: Source IDs are now added to snapshots
- Fix: InMemoryReadStore will not break on unmodified update result
- Breaking: Elasticsearch NEST Nuget Library updated from v6.1.0 to v7.8.2
- New: Now possible to implement error handlers for specific sagas using
ISagaErrorHandler<TSaga>
- Fixed: You can now create
Id : Identity<Id>
- Breaking: To support .NET going forward, all EventFlow test have been converted
from .NET Framework 4.x to .NET Core 3.1. This however, introduced a set of
breaking changes
- EntityFramework has been updated from 2.2.6 to 3.1.5
IHangfireJobRunner.Execute
is nowIHangfireJobRunner.ExecuteAsync
- Breaking: Merged
AggregateReadStoreManager
andSingleAggregateReadStoreManager
into one class in order to always guarantee in-order event processing - Breaking: Marked the
UseReadStoreFor<,,,>
configuration methods as obsolete, in favor of the simpler overloads with less type parameters (as those automatically figure out the AggregateRoot and Id types and configure the more reliableSingleAggregateReadStoreManager
implementation) - Obsolete: The class
AsyncHelper
and all non-async methods using it have been marked obsolete and will be removed in EventFlow 1.0 (not planned yet). If you rely on these non-async methods, then merely copy-paste theAsyncHelper
from the EventFlow code base and continue using it in your transition to async only - Fixed: An issue where
EntityFrameworkEventPersistence
could possibly save aggregate events out of order, which would lead to out-of-order application when streaming events ordered by GlobalSequenceNumber - New:
FilesEventPersistence
now uses relative paths - New: A new set of hook-in interfaces are provided from this release, which should
make it easier to implement crash resilience (#439) in EventFlow. Please note that
this new API is experimentational and subject to change as different strategies are
implemented
IAggregateStoreResilienceStrategy
IDispatchToReadStoresResilienceStrategy
IDispatchToSubscriberResilienceStrategy
ISagaUpdateResilienceStrategy
- New: Added .NET Core 3.1 target for the
EventFlow
andEventFlow.EntityFramework
packages - Added quoting to the SQL query generator for the column names
- New: Updated LibLog provider to support structured logging with NLog 4.5. Reduced memory allocations for log4net-provider
- New: Made several methods in
AggregateRoot<,>
virtual
to allow easier customization - Fixed: Added quoting to the SQL query generator for the column names
-- query before the fix
UPDATE [ReadModel-TestAttributes]
SET UpdatedTime = @UpdatedTime
WHERE Id = @Id
-- query after the fix
UPDATE [ReadModel-TestAttributes]
SET [UpdatedTime] = @UpdatedTime
WHERE [Id] = @Id
- Fixed: Do not log about event upgraders if none is found for an event
- Fixed: Add default
null
predicate toAddCommands
andAddJobs
- New: The
EventFlow.AspNetCore
NuGet package now has ASP.NET Core 3 support
- New: Mongo DB read model store Queryable:
MongoDbReadModelStore readModelStore; IQueryable<TReadModel> queryable = readModelStore.AsQueryable();
- New: Moved publish of messages in
RabbitMqPublisher
to a new virtual method to ease reuse and customization - Fixed: MongoDB read models no longer has the
new()
generic requirement, which aligns read model requirements with the rest of EventFlow
- Fix: When deserializing the JSON value
"null"
into a struct value likeint
, theSingleValueObjectConverter
threw an exception instead of merely returningnull
representing an absentSingleValueObject<int>
value
- Breaking: Renamed
AspNetCoreEventFlowOptions.AddMetadataProviders()
toAddDefaultMetadataProviders()
and madeAddUserClaimsMetadata
opt-in in order to prevent policy issues. - Fix: Allow explicit implementations of
IEmit<>
in aggregate roots - Fix: Using
.AddAspNetCore()
with defaults now doesn't throw a DI exception.
- New: Configure JSON serialization:
EventFlowOptions.New. .ConfigureJson(json => json .AddSingleValueObjects() .AddConverter<SomeConverter>() )
- New: ASP.NET Core enhancements:
- New fluent configuration API for ASP.NET Core components:
services.AddEventFlow(o => o.AddAspNetCore(c => {...}));
(old syntaxAddAspNetCoreMetadataProviders
is now deprecated). .RunBootstrapperOnHostStartup()
runs bootstrappers together with ASP.NET host startup. Previously, this was done inAddAspNetCoreMetadataProviders
and led to some confusion..UseMvcJsonOptions()
adds EventFlow JSON configuration (see below) to ASP.NET Core, so you can accept and return Single Value Objects as plain strings for example..Add{Whatever}Metadata()
configures specific metadata provider..AddUserClaimsMetadata(params string claimTypes)
configures the new claims metadata provider (for auditing or "ChangedBy" in read models)..UseLogging()
configures an adapter for Microsoft.Extensions.Logging.UseModelBinding()
adds model binding support for Single Value Objects:[HttpGet("customers/{id}")] public async Task<IActionResult> SingleValue(CustomerId id) { if (!ModelState.IsValid) { return BadRequest(ModelState); }
- New fluent configuration API for ASP.NET Core components:
- Fix: ASP.NET Core
AddRequestHeadersMetadataProvider
doesn't throw when HttpContext is null. - Fix:
ReadModelRepopulator
now correctly populatesIAmAsyncReadModelFor
- New:
EventFlow.TestHelpers
are now released as .NET Standard as well - Fix: Upgrade
EventStore.Client
to v5.0.1 and use it for both .NET Framework and .NET Core - Fix: Storing events in MS SQL Server using
MsSqlEventPersistence
now correctly handles non-ANSI unicode characters in strings. - Fix: Source link integration now works correctly
- Breaking: Commands published from AggregateSaga which return
false
inIExecutionResult.IsSuccess
will newly lead to an exception being thrown. For disabling all new changes just set protected propertyAggregateSaga.ThrowExceptionsOnFailedPublish
tofalse
in your AggregateSaga constructor. Also an Exception thrown from any command won't prevent other commands from being executed. All exceptions will be collected and then re-thrown in SagaPublishException (even in case of just one Exception). The exception structure is following:- SagaPublishException : AggregateException
- .InnerExceptions
- CommandException : Exception
- .CommandType
- .SourceId
- .InnerException # in case of an exception thrown from the command
- CommandException : Exception
- .CommandType
- .SourceId
- .ExecutionResult # in case of returned
false
inIExecutionResult.IsSuccess
You need to update yourISagaErrorHandler
implementation to reflect new exception structure, unless you disable this new feature.
- CommandException : Exception
- .InnerExceptions
- SagaPublishException : AggregateException
- Fix: MongoDB read store no longer throws an exception on non-existing read models (#625)
- Breaking: Changed target framework to to .NET Framework 4.5.2 for the following NuGet packages,
as Microsoft has discontinued
support for .NET Framework 4.5.1
EventFlow
EventFlow.TestHelpers
EventFlow.Autofac
EventFlow.Elasticsearch
EventFlow.Examples.Shipping
EventFlow.Examples.Shipping.Queries.InMemory
EventFlow.Hangfire
EventFlow.MongoDB
EventFlow.MsSql
EventFlow.Owin
EventFlow.PostgreSql
EventFlow.RabbitMQ
EventFlow.Sql
EventFlow.SQLite
- New: Added SourceLink support
- Fix:
DispatchToSagas.ProcessSagaAsync
useEventId
instead ofSourceId
asSourceId
for delivery of external event to AggregateSaga - Fix:
Identity<T>.NewComb()
now produces string values that doesn't cause too much index fragmentation in MSSQL string columns
- New: Added configuration option to set the "point of no return" when using
cancellation tokens. After this point in processing, cancellation tokens
are ignored:
options.Configure(c => c.CancellationBoundary = CancellationBoundary.BeforeCommittingEvents)
- New: Added
EventFlowOptions.RunOnStartup<TBootstrap>
extension method to registerIBootstrap
types that should run on application startup. - New: Support for async read model updates (
IAmAsyncReadModelFor
). You can mix and match asynchronous and synchronous updates, as long as you don't subscribe to the same event in both ways. - Fix: Added the schema
dbo
to theeventdatamodel_list_type
in script0002 - Create eventdatamodel_list_type.sql
forEventFlow.MsSql
. - Fix:
LoadAllCommittedEvents
now correctly handles cases where theGlobalSequenceNumber
column contains gaps larger than the page size. This bug lead to incomplete event application when using theReadModelPopulator
(see #564). - Fix:
IResolver.Resolve<T>()
andIResolver.Resolve(Type)
now throw an exception for unregistered services when usingEventFlow.DependencyInjection
. - Minor fix: Fixed stack overflow in
ValidateRegistrations
when decorator components are co-located together with other components that are registed usingAdd*
-methods
- Breaking: Changed name of namespace of the projects AspNetCore
EventFlow.Aspnetcore
toEventFlow.AspNetCore
- Fix: Ignore multiple loads of the same saga
- New: Expose
Lifetime.Scoped
through the EventFLow service registration interface - New: Upgrade NEST version to 6.1.0 and Hangfire.Core to 1.6.20
Now Elasticsearch provide one index per document. If
ElasticsearchTypeAttribute
is used the index is map with the Name value as an alias. WhenElasticsearchReadModelStore
delete all documents, it will delete all indexes linked to the alias. - Fix: Internal IoC (remember its just for testing) now correctly invokes
IDisposable.Dispose()
on scope and container dispose
- Critical fix: - fix issue where the process using EventFlow could hang using 100% CPU due to unsynchronized Dictionary access, See #541.
- New: Entity Framework Core support in the form of the new
EventFlow.EntityFramework
NuGet package. It has been tested with the following stacks.- EF Core In-Memory Database Provider
- SQLite
- SQL Server
- PostgreSQL
- Minor: Performance improvement of storing events for
EventFlow.PostgreSql
- New: Added .NET standard support for SQLite
- New: PostgreSQL support in the form of the new
EventFlow.PostgreSql
NuGet package
- New: Created
AggregateReadStoreManager<,,,>
which is a new read store manager for read models that have a 1-to-1 relation with an aggregate. If read models get out of sync, or events are applied in different order, events are either fecthed or skipped. Added extensions to allow registration.UseInMemoryReadStoreFor<,,>
UseElasticsearchReadModelFor<,,>
UseMssqlReadModelFor<,,>
UseSQLiteReadModelFor<,,>
- New: Added
ReadModelId
andIsNew
properties to the context object that is available to a read model inside theApply
methods in order to better support scenarios where a single event affects multiple read model instances. - Minor: Applying events to a snapshot will now have the correct
Version
set inside theApply
methods. - Minor: Trying to apply events in the wrong order will now throw an exception.
-
New: Support for
Microsoft.Extensions.DependencyInjection
(IServiceProvider
andIServiceCollection
) using theEventFlow.DependencyInjection
NuGet package.Add it to your ASP.NET Core 2.0 application:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddEventFlow(o => o.AddDefaults(MyDomainAssembly)); }
Or use it explicitly:
EventFlowOptions.New. .UseServiceCollection() ... .CreateServiceProvider();
-
New: Package
EventFlow.Autofac
now references Autofac 3.5.2 for .NET framework 4.5.1 (down from Autofac v4.5.0) -
Fixed: Constructor injection of scoped instances into query handlers
- New: Implemented optimistic concurrency checks for MSSQL, SQLite and Elasticsearch read models
- New: Added .NET standard support for EventStore
- New: Delete read models by invoking
context.MarkForDeletion()
in an Apply method - Minor: Removed unnecessary transaction in EventStore persistance
- Fixed: Read model SQL schema is no longer ignored for
Table
attribute
- Fix: Commands are now correctly published when no events are emitted from a saga after handling a domain event
- Minor fix: Updated name of Primary Key for MSSQL Snapshot Store to be different from MSSQL Event Store, so both can be used in the same database without conflicts
- Minor fix: Corrected log in
CommandBus
regarding events emitted due to command
- Fixed: AggregateException/InvalidOperationException when reading and updating
an aggregate from different threads at the same time using
InMemoryEventPersistence
- New: .NET standard 1.6 and 2.0 support for
EventFlow.MsSql
package
- New: Allow enums to be used in
SingleValueObject<T>
and protect from undefined enum values
- Fixed: Re-populating events to read models that span multiple aggregates now has events orderd by timestamp instead of sequence numbers, i.e., events from aggregates with higher sequences numbers isn't forced last
- New: Trigger sagas without the need of any domain events by using
ISagaStore.UpdateAsync(...)
- New: .NET standard 2.0 (still supports 1.6) support added to these
NuGet packages
- EventFlow
- EventFlow.Autofac
- EventFlow.Elasticsearch
- EventFlow.Hangfire
- EventFlow.Sql
- Critical fix:
SagaAggregateStore
was incorrectly putting an object reference into its memory cache causing an object already disposed exception when working with sagas - New: Added LibLog, enable by
calling the
IEventFlowOptions.UseLibLog(...)
extension
- New: Allow events to have multiple
EventVersion
attributes - Fixed:
ReflectionHelper.CompileMethodInvocation
now recognisesprivate
methods.
- Fixed:
.UseFilesEventStore
now uses a thread safe singleton instance for file system persistence, making it suitable for use in multi-threaded unit tests. Please don't use the files event store in production scenarios - New: Support for unicode characters in type names. This simplifies using an ubiquitous language in non-english domains
- Fixed: Include hyphen in prefix validation for identity values. This fixes a bug
where invalid identities could be created (e.g.
ThingyId.With("thingyINVALID-a41e...")
)
- New: Removed the
new()
requirement for read models - New: If
ISagaLocator.LocateSagaAsync
cannot identify the saga for a given event, it may now returnTask.FromResult(null)
in order to short-circuit the dispatching process. This might be useful in cases where some instances of an event belong to a saga process while others don't - Fixed:
StringExtensions.ToSha256()
can now be safely used from concurrent threads
-
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.PublishAsync
signature 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.SourceId
is now of typeISourceId
instead of using the generic type and theICommandBus.PublishAsync
no longer returnsTask<ISourceId>
To get code that behaves similar to the previous version, simply take the
ISourceId
from the command, i.e., instead of thisvar sourceId = await commandBus.PublishAsync(command);
write this
await commandBus.PublishAsync(command); var sourceId = command.SourceId;
(
CancellationToken
and.ConfigureAwait(false)
omitted fromt he above) -
Breaking: Upgraded NuGet dependency on
RabbitMQ.Client
from>= 4.1.3
to>= 5.0.1
- Breaking: Upgraded
EventStore.Client
dependency to version 4.0 - Breaking: Changed target framework for
EventFlow.EventStores.EventStore
to .NET 4.6.2 as required byEventStore.Client
NuGet dependency - Fix:
EventFlow.Hangfire
now depends onHangfire.Core
instead ofHangfire
- New: Added an overload to
IDomainEventPublisher.PublishAsync
that isn't generic and doesn't require an aggregate ID - New: Added
IReadModelPopulator.DeleteAsync
that 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
IReadModelPopulator
to extension methods - New: Added non-generic overloads for purge and populate methods on
IReadModelPopulator
- New: Provided
EventFlow.TestHelpers
which 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
,18401
and40540
as well as a few nativeWin32Exception
exceptions 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.AddSubscribers
andIEventFlowOpions.AddDefaults
now correctly registers implementations ofISubscribeAsynchronousTo<,,>
- Obsolete:
IEventFlowOpions.AddSubscriber
is 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);
ITaskRunner
has 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 theITaskRunner
led 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.Hangfire
NuGet 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 theInstantJobScheduler
behaves as any other out-of-process job scheduler
- New: .NET Standard 1.6 support for the following NuGet packages
EventFlow
EventFlow.Autofac
EventFlow.Elasticsearch
EventFlow.Hangfire
EventFlow.RabbitMQ
- Fixed: Removed dependency
Microsoft.Owin.Host.HttpListener
fromEventFlow.Owin
as 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.EventId
is 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 support
to 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
EventFlow
core 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.Autofac
and 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.MsSql
packageEventFlow.EventStores.MsSql
EventFlow.ReadStores.MsSql
- Breaking:
ITaskRunner.Run(...)
has changed signature. The task factory now gets an instance ofIResolver
that 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
ITaskRunner
for 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 useITaskRunner
in very limited cases, e.g. if there's implantations ofISubscribeAsynchronousTo<,,>
- Fixed:
IAggregateStore.UpdateAsync
andStoreAsync
now publishes committed events as expected. This basically means that its now possible to circumvent the command and command handler pattern and use theIAggregateStore.UpdateAsync
directly 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
IMemoryCache
for 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 onIEventFlowOptions
to 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.2163
by adding NuGet package referenceDbUp
toEventFlow.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.Net
v2.3.3 (up from v1.7.1)Elasticsearch.Net.JsonNET
removedNEST
v2.3.3 (up from v1.7.1)Newtonsoft.Json
v8.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
IEventStore
ICommandBus
- New: EventFlow can now be configured to throw exceptions thrown by subscribers
by
options.Configure(c => c.ThrowSubscriberExceptions = true)
- New: Added an
ICommandScheduler
for easy scheduling of commands
- Breaking: To simplify the EventFlow NuGet package structure, the two NuGet
packages
EventFlow.EventStores.MsSql
andEventFlow.ReadStores.MsSql
have 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
ConfigureElasticsearch
that takes multiple of URLs,SniffingConnectionPool
is now used instead ofStaticConnectionPool
and with sniff life span of five minutes
- Breaking:
IAggregateRoot
has some breaking changes. If these methods aren't used (which is considered the typical case), then the base classAggregateRoot<,,>
will automatically handle itCommitAsync
has an additionalISnapshotStore
parameter. If you don't use snapshot aggregates, then you can safely passnull
LoadAsync
is 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
IAggregateStore
is introduced, which provides a cleaner interface for manipulating aggregate roots. The most important method is theUpdateAsync
which allows easy updates to aggregate roots without the need for a command and command handlerLoadAsync
UpdateAsync
StoreAsync
- New:
IEventStore
now supports loading events from a specific version using the new overload ofLoadEventsAsync
that takes afromEventSequenceNumber
argument - New:
IMsSqlDatabaseMigrator
now has a overloaded method namedMigrateDatabaseUsingScripts
that 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
IEventStore
has been marked as obsolete in favor of the newIAggregateStore
. The methods will be removed at some point in the futureLoadAggregateAsync
LoadAggregate
- Critical fix:
OptimisticConcurrencyRetryStrategy
now correctly only states thatOptimisticConcurrencyException
should 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 ownIOptimisticConcurrencyRetryStrategy
that has the old behavior - Fixed:
OptimisticConcurrencyRetryStrategy
has 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 IMetadataProvider
from 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
MssqlMigrationException
toSqlMigrationException
- Breaking: Renamed
SqlErrorRetryStrategy
toMsSqlErrorRetryStrategy
as its MSSQL specific - Breaking: The NuGet package
Dapper
is no longer IL merged with the packageEventFlow.MsSql
but is now listed as a NuGet dependency. The current version used by EventFlow isv1.42
- New: Introduced the NuGet package
EventFlow.SQLite
that adds event store support for SQLite databases, both as event store and read model store - New: Introduced the NuGet package
EventFlow.Sql
as 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
AsyncHelper
if e.g. an exception caused noasync
tasks to be scheduled. TheAsyncHelper
is used by EventFlow to expose non-async
methods to developers and provide the means to callasync
methods from a synchronous context without causing a deadlock. There's no change to any of theasync
methods.The
AsyncHelper
is used in the following methods.ICommandBus.Publish
IEventStore.LoadEvents
IEventStore.LoadAggregate
IEventStore.LoadAllEvents
IJobRunner.Execute
IReadModelPopulator.Populate
IReadModelPopulator.Purge
IQueryProcessor.Process
- Breaking: The following NuGet references have been updated
EventStore.Client
v3.4.0 (up from v3.0.2)Hangfire.Core
v1.5.3 (up from v1.4.6)RabbitMQ.Client
v3.6.0 (up from v3.5.4)
- New: EventFlow now uses Paket to manage NuGet packages
- Fixed: Incorrect use of
EventStore.Client
that caused it to throwWrongExpectedVersionException
when 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.RabbitMQ
EventFlow.EventStores.EventStore
- Fixed: Updated internal NuGet reference
dbup
to v3.3.0 (up from v3.2.1)
- Breaking: EventFlow no longer ignores columns named
Id
in MSSQL read models. If you were dependent on this, use theMsSqlReadModelIgnoreColumn
attribute - Fixed: Instead of using
MethodInfo.Invoke
to 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
ArgumentException
if 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 emptyIReadModel
interface. 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 requiredMsSqlReadModelIdentityColumn
attribute to mark the identity column and the optional (but recommended)MsSqlReadModelVersionColumn
to mark the version column.string AggregateId
DateTimeOffset CreateTime
DateTimeOffset UpdatedTime
int LastAggregateSequenceNumber
- Obsolete:
IMssqlReadModel
andMssqlReadModel
. Developers should instead use theMsSqlReadModelIdentityColumn
andMsSqlReadModelVersionColumn
attributes 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.Resolver
to 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 fromValueObject
but uses only theId
field as equality component. OverrideGetEqualityComponents()
if you have a different notion of equality for a specific entity - Breaking:
Entity<T>
will now throw anArgumentNullException
if theid
passed to its constructor isnull
- Breaking: Fixed method spelling. Renamed
ISpecification<T>.WhyIsNotStatisfiedBy
toWhyIsNotSatisfiedBy
andSpecification<T>.IsNotStatisfiedBecause
toIsNotSatisfiedBecause
- New: Read model support for Elasticsearch via the new NuGet package
EventFlow.ReadStores.Elasticsearch
- Breaking:
AddDefaults
now 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
,ICommandDefinitonService
andIJobsDefinitonService
now 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.format
if 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 newIFilesEventLocator
and provide your own custom event file layout. - Breaking: Event stores have been split into two parts, the
IEventStore
and the newIEventPersistence
.IEventStore
has the same interface before but the implementation is now no longer responsible for persisting the events, only converting and serializing the persisted events.IEventPersistence
handles the actual storing of events and thus if any custom event stores have been implemented, they should implement to the newIEventPersistence
instead. - New: Added
IEntity
,IEntity<>
and an optionalEntity<>
that developers can use to implement DDD entities.
- Fixed: Using NuGet package
EventFlow.Autofac
causes an exception with the messageThe type 'EventFlow.Configuration.Registrations.AutofacStartable' is not assignable to service 'Autofac.IStartable
during EventFlow setup
- Breaking: Removed
HasRegistrationFor<>
andGetRegisteredServices()
fromIServiceRegistration
and added them toIResolver
instead. The methods required that all service registrations went through EventFlow, which in most cases they will not - Obsolete: Marked
IServiceRegistration.RegisterIfNotRegistered(...)
, use thekeepDefault = true
on 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.Autofac
package) completely. EventFlow will register its bootstrap services in the IoC container and configure itself whenever the container is created - New: Introduced
IBootstrap
interface that you can register. It has a singleBootAsync(...)
method that will be called as soon as the IoC container is ready (similar to that ofIStartable
of 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
EventFlowOptions
extensions are nowIEventFlowOptions
instead andEventFlowOptions
implements 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
IModule
and 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.Hangfire
NuGet package. Read the jobs documentation for more details - New: Created the OWIN
CommandPublishMiddleware
middleware that can handle publishing of commands by posting a JSON serialized command to e.g./commands/ping/1
in whichping
is the command name and1
its 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.SourceId
being 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 B
and thenevent A
- Breaking: Aggregate root no longer have
Aggregate
removed from their when name, i.e., the metadata property with keyaggregate_name
(orMetadataKeys.AggregateName
). If you are dependent on the previous naming, use the newAggregateName
attribute and apply it to your aggregates - Breaking: Moved
Identity<>
andIIdentity
from theEventFlow.Aggregates
namespace toEventFlow.Core
as the identities are not specific for aggregates - Breaking:
ICommand.Id
is renamed toICommand.AggregateId
to make "room" for the newICommand.SourceId
property. 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 additionalISourceId
argument. 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 newCommandId
each 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.Name
property - 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 anArgumentException
instead of aTargetInvocationException
when passed an invalid identity - Fixed: Aggregate roots now build the cache of
Apply
methods 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.RabbitMQ
which 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.Json
to v7.0.1 (up from v6.0.8) - Breaking: Remove the empty constructor from
SingleValueObject<>
- New: Added
SingleValueObjectConverter
to 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 beprivate
andprotected
- 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
Dapper
to v1.42 (up from v1.38)
- Braking:
IEventStore.LoadAllEventsAsync
andIEventStore.LoadAllEvents
now take aGlobalPosition
as an argument instead of along
for the starting position. TheGlobalPosition
is basically a wrapper around a string that hides the inner workings of each event store. - New: NuGet package
EventFlow.EventStores.EventStore
that 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
LastGlobalSequenceNumber
column during update, or set it to defaultNULL
IDomainEvent.GlobalSequenceNumber
removedIEventStore.LoadEventsAsync
andIEventStore.LoadEvents
taking aGlobalSequenceNumberRange
removed
- 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.BatchId
to metadata and createdMetadataKeys.BatchId
to help access it -
New:
IEventStore.DeleteAggregateAsync
to 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:
IReadModelPopulator
is 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:
IEventStore
now hasLoadAllEventsAsync
andLoadAllEvents
that enables you to load all events in the event store a few at a time. -
New:
IMetadata.TimestampEpoch
contains the Unix timestamp version ofIMetadata.Timestamp
. Also, an additional metadata keytimestamp_epoch
is added to events containing the same data. Note, theTimestampEpoch
onIMetadata
handles cases in which thetimestamp_epoch
is 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 anIEventUpgrader
removed events from the event stream -
Fixed:
InMemoryReadModelStore<,>
is now thread safe
- New: EventFlow now includes a
IQueryProcessor
that 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
IReadModelLocator
to define how read models are located. The suppliedILocateByAggregateId
simply uses the aggregate ID. To subscribe to other events, simply implementIAmReadModelFor<,,>
and make sure you have supplied a proper read model locator.UseMssqlReadModel
signature changed, change to.UseMssqlReadModel<MyReadModel, ILocateByAggregateId>()
in order to have the previous functionalityUseInMemoryReadStoreFor
signature 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:
ITransientFaultHandler
now 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
IReadStoreManager
if you would like to implement your own read model handling - New:
IEventStore
now has aLoadEventsAsync
andLoadEvents
that loadsIDomainEvent
s 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
TableAttribute
fromSystem.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
int
in 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!
IAggregateRoot
changed 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.Publish
now takes aCancellationToken
argument - Fixed: MSSQL should list columns to SELECT when fetching events
- Breaking:
ValueObject
now uses public properties instead of both private and public fields - Breaking: Aggregate IDs are no longer
string
but objects implementingIIdentity
- Breaking: MSSQL transient exceptions are now retried
- Breaking: All methods on
IMsSqlConnection
has an extraLabel
argument - New:
ITransientFaultHandler
added 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
ValueObject
when claculating hash - Fixed:
NullReferenceException
thrown ifnull
was stored inSingleValueObject
andToString()
was called
- First stable version of EventFlow