Skip to content

Commit

Permalink
Merge pull request #927 from eventflow/async-event-upgrader
Browse files Browse the repository at this point in the history
v1: Async event upgrader upgrade
  • Loading branch information
rasmus authored Nov 11, 2022
2 parents 3690420 + f82e189 commit acc2317
Show file tree
Hide file tree
Showing 29 changed files with 883 additions and 217 deletions.
13 changes: 11 additions & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ that wasn't possible to add before as introducing them would cause breaking chan

This allows for connection strings to be fetched runtime from external sources.

- **Event upgraders are now async and works with the read model populator:** In
version 0.x, event upgraders aren't applied when events are loaded and
re-populated to read models using the `IReadModelPopulator`. This is fixed for
version 1.x, which properly will require some change to upgraders if the
re-population feature is used.


## Changes to supported .NET versions

With the 1.x release, EventFlow limits the amount of supported .NET versions, to
Expand Down Expand Up @@ -82,6 +89,10 @@ of EventFlow from 0.x to 1.x.
- Since there is no change to the underlying storage, creating a release that
only has EventFlow upgraded is highly recommended. This enables easy rollback
if you encounter unexpected problems
- Since the `IEventUpgrader<,>` has changed significantly, consider using the new
base class `EventUpgraderNonAsync` in any existing upgraders. It provides an
`abstract` method with the same signature as the old interface that can be
overridden, making the switch significantly easier

## NuGet packages removed

Expand Down Expand Up @@ -182,5 +193,3 @@ environments.
- `IQueryProcessor.Process`
- `IReadModelPopulator.Populate`
- `IReadModelPopulator.Purge`


22 changes: 20 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ as recommendations on how to do the migration.

https://github.com/eventflow/EventFlow/blob/develop-v1/MIGRATION_GUIDE.md

Changes since 1.0.5001-alpha

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 class `EventUpgraderNonAsync` for any existing upgraders. Its a `abstract`
class that implements the updated interface and provides a `abstract` 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 new `IEventUpgradeContextFactory`. 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 the
`DuplicateOperationException` to be correctly thrown if a duplicate source
Expand All @@ -31,6 +40,15 @@ Complete 1.0 change log
* 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 class `EventUpgraderNonAsync` for any existing upgraders. Its a `abstract`
class that implements the updated interface and provides a `abstract` 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 new `IEventUpgradeContextFactory`. Replace this if you need addition
context during event upgrades
* New: Its now possible to change the execution timeout for database migrations using the
`SetUpgradeExecutionTimeout(...)` on the SQL configuration
* Breaking: Removed the following dead and/or confusion MSSQL attributes. The real ones
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// https://github.com/eventflow/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System.Threading;
using System.Threading.Tasks;
using EventFlow.Commands;

namespace EventFlow.TestHelpers.Aggregates.Commands
{
public class ThingyEmitUpgradableEventsCommand : Command<ThingyAggregate, ThingyId>
{
public int UpgradableEventV1Count { get; }
public int UpgradableEventV2Count { get; }
public int UpgradableEventV3Count { get; }

public ThingyEmitUpgradableEventsCommand(
ThingyId aggregateId,
int upgradableEventV1Count,
int upgradableEventV2Count,
int upgradableEventV3Count)
: base(aggregateId)
{
UpgradableEventV1Count = upgradableEventV1Count;
UpgradableEventV2Count = upgradableEventV2Count;
UpgradableEventV3Count = upgradableEventV3Count;
}
}

public class ThingyEmitUpgradableEventsCommandHandler : CommandHandler<ThingyAggregate, ThingyId, ThingyEmitUpgradableEventsCommand>
{
public override Task ExecuteAsync(
ThingyAggregate aggregate,
ThingyEmitUpgradableEventsCommand command,
CancellationToken cancellationToken)
{
aggregate.EmitUpgradableEvents(
command.UpgradableEventV1Count,
command.UpgradableEventV2Count,
command.UpgradableEventV3Count);

return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2021 Rasmus Mikkelsen
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// Copyright (c) 2015-2021 eBay Software Foundation
// https://github.com/eventflow/EventFlow
//
Expand Down Expand Up @@ -30,7 +30,7 @@ namespace EventFlow.TestHelpers.Aggregates.Events
[EventVersion("ThingyPing", 1)]
public class ThingyPingEvent : AggregateEvent<ThingyAggregate, ThingyId>
{
public PingId PingId { get; private set; }
public PingId PingId { get; }

public ThingyPingEvent(PingId pingId)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// https://github.com/eventflow/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using EventFlow.Aggregates;

namespace EventFlow.TestHelpers.Aggregates.Events
{
public class ThingyUpgradableV1Event : AggregateEvent<ThingyAggregate, ThingyId>
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// https://github.com/eventflow/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using EventFlow.Aggregates;

namespace EventFlow.TestHelpers.Aggregates.Events
{
public class ThingyUpgradableV2Event : AggregateEvent<ThingyAggregate, ThingyId>
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// https://github.com/eventflow/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using EventFlow.Aggregates;

namespace EventFlow.TestHelpers.Aggregates.Events
{
public class ThingyUpgradableV3Event : AggregateEvent<ThingyAggregate, ThingyId>
{
}
}
43 changes: 42 additions & 1 deletion Source/EventFlow.TestHelpers/Aggregates/ThingyAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ namespace EventFlow.TestHelpers.Aggregates
[AggregateName("Thingy")]
public class ThingyAggregate : SnapshotAggregateRoot<ThingyAggregate, ThingyId, ThingySnapshot>,
IEmit<ThingyDomainErrorAfterFirstEvent>,
IEmit<ThingyDeletedEvent>
IEmit<ThingyDeletedEvent>,
IEmit<ThingyUpgradableV1Event>,
IEmit<ThingyUpgradableV2Event>,
IEmit<ThingyUpgradableV3Event>
{
// ReSharper disable once NotAccessedField.Local
private readonly IScopedContext _scopedContext;
Expand All @@ -58,6 +61,10 @@ public class ThingyAggregate : SnapshotAggregateRoot<ThingyAggregate, ThingyId,
public IReadOnlyCollection<ThingySnapshotVersion> SnapshotVersions { get; private set; } = new ThingySnapshotVersion[] {};
public bool IsDeleted { get; private set; }

public int UpgradableEventV1Received { get; private set; }
public int UpgradableEventV2Received { get; private set; }
public int UpgradableEventV3Received { get; private set; }

public ThingyAggregate(ThingyId id, IScopedContext scopedContext)
: base(id, SnapshotEveryFewVersionsStrategy.With(SnapshotEveryVersion))
{
Expand Down Expand Up @@ -130,11 +137,45 @@ public void RequestSagaException()
Emit(new ThingySagaExceptionRequestedEvent());
}

public void EmitUpgradableEvents(
int upgradableEventV1Count,
int upgradableEventV2Count,
int upgradableEventV3Count)
{
void EmitCount<T>(int count)
where T : IAggregateEvent<ThingyAggregate, ThingyId>, new()
{
for (var i = 0; i < count; i++)
{
Emit(new T());
}
}

EmitCount<ThingyUpgradableV1Event>(upgradableEventV1Count);
EmitCount<ThingyUpgradableV2Event>(upgradableEventV2Count);
EmitCount<ThingyUpgradableV3Event>(upgradableEventV3Count);
}

public void Apply(ThingyDomainErrorAfterFirstEvent e)
{
DomainErrorAfterFirstReceived = true;
}

public void Apply(ThingyUpgradableV1Event aggregateEvent)
{
UpgradableEventV1Received++;
}

public void Apply(ThingyUpgradableV2Event aggregateEvent)
{
UpgradableEventV2Received++;
}

public void Apply(ThingyUpgradableV3Event _)
{
UpgradableEventV3Received++;
}

void IEmit<ThingyDeletedEvent>.Apply(ThingyDeletedEvent e)
{
IsDeleted = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2022 Rasmus Mikkelsen
// https://github.com/eventflow/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System.Collections.Generic;
using EventFlow.Aggregates;
using EventFlow.EventStores;
using EventFlow.TestHelpers.Aggregates.Events;

namespace EventFlow.TestHelpers.Aggregates.Upgraders
{
public class ThingyUpgradableV1ToV2EventUpgrader : EventUpgraderNonAsync<ThingyAggregate, ThingyId>
{
private readonly IDomainEventFactory _domainEventFactory;

public ThingyUpgradableV1ToV2EventUpgrader(
IDomainEventFactory domainEventFactory)
{
_domainEventFactory = domainEventFactory;
}

protected override IEnumerable<IDomainEvent<ThingyAggregate, ThingyId>> Upgrade(
IDomainEvent<ThingyAggregate, ThingyId> domainEvent)
{
if (!(domainEvent is IDomainEvent<ThingyAggregate, ThingyId, ThingyUpgradableV1Event> _))
{
yield return domainEvent;
}
else
{
yield return _domainEventFactory.Upgrade<ThingyAggregate, ThingyId>(
domainEvent,
new ThingyUpgradableV2Event());
}
}
}
}
Loading

0 comments on commit acc2317

Please sign in to comment.