From 50ac7d671b52623424194c89637d22582b17bb25 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Fri, 30 Sep 2016 17:01:59 +0300 Subject: [PATCH 01/33] Newtonsoft JSON - based event adapter with field update capability. --- .../SubObject_version_upgrade.cs | 146 ++++++++++++++++++ .../GridDomain.Tests.csproj | 1 + .../DomainEventAdapters/DomainEventAdapter.cs | 2 +- .../DomainEventSerialization.cs | 32 ++++ .../IDomainEventAdapter.cs | 4 +- .../GridDomain.EventSourcing.csproj | 5 + GridDomain.EventSourcing/packages.config | 4 +- 7 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs create mode 100644 GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs diff --git a/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs new file mode 100644 index 00000000..ee4a2828 --- /dev/null +++ b/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization.Formatters; +using System.Text; +using System.Threading.Tasks; +using GridDomain.EventSourcing.DomainEventAdapters; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace GridDomain.Tests.EventsUpgrade +{ + abstract class ObjectConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + + if (reader.Value != null && reader.ValueType == typeof(TFrom)) + { + TFrom convertFromValue = (TFrom)reader.Value; + return Convert(convertFromValue); + } + if (reader.TokenType == JsonToken.StartObject && existingValue == null) + { + var jObject = JObject.Load(reader); + + var type = PeekType(jObject); + if (type != typeof(TFrom)) + return serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); + + existingValue = serializer.ContractResolver.ResolveContract(type).DefaultCreator(); + serializer.Populate(jObject.CreateReader(), existingValue); + + return Convert((TFrom)existingValue); + } + if (reader.TokenType == JsonToken.Null) + { + return null; + } + throw new JsonSerializationException(); + } + + private static Type PeekType(JObject jobject) + { + string typeName; + if (string.IsNullOrEmpty(typeName = jobject["$type"]?.ToObject())) + throw new TypeNameNotFoundException(); + + var type = Type.GetType(typeName); + return type; + } + + protected abstract TTo Convert(TFrom value); + + public override bool CanWrite => false; + public override bool CanRead => true; + + public override bool CanConvert(Type objectType) + { + return typeof(TDeclared) == objectType; + } + } + + [TestFixture] + class SubObject_version_upgrade + { + private interface ISubObject + { + string Value { get; set; } + } + + class SubObject_V1 : ISubObject + { + public string Name { get; set; } + public string Value { get; set; } + } + + class SubObject_V2 : ISubObject + { + public int number { get; set; } + public string Value { get; set; } + } + + class OldEvent + { + public ISubObject Payload { get; set; } + } + + class SubObjectCoverter : ObjectConverter + { + protected override SubObject_V2 Convert(SubObject_V1 value) + { + return new SubObject_V2() { number = int.Parse(value.Name), Value = value.Value }; + } + } + + + + [Test] + public void Given_persisted_event_with_old_subobject_It_can_be_updated_to_new_subobject() + { + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectCoverter()); + + var serializedValue = @"{ + ""$id"": ""1"", + ""Payload"": { + ""$id"": ""2"", + ""$type"": ""GridDomain.Tests.EventsUpgrade.SubObject_version_upgrade+SubObject_V1, GridDomain.Tests"", + ""Name"": ""10"", + ""Value"": ""123"" + } + }"; + + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + Assert.IsInstanceOf(restoredEvent.Payload); + } + + + [Test] + public void Given_event_with_old_subobject_it_Should_be_updated_to_new_subobject_by_subobject_adapter() + { + var initialEvent = new OldEvent() {Payload = new SubObject_V1() {Name = "10",Value = "123"} }; + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectCoverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue,settings); + + Assert.IsInstanceOf(restoredEvent.Payload); + } + } + + internal class TypeNameNotFoundException : Exception + { + } +} diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 2650e0be..d6f94a54 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -288,6 +288,7 @@ + diff --git a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs b/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs index 18d9fe84..ccbc9360 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs +++ b/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs @@ -3,7 +3,7 @@ namespace GridDomain.EventSourcing.DomainEventAdapters { - public abstract class DomainEventAdapter : IDomainEventAdapter where TFrom : DomainEvent where TTo : DomainEvent + public abstract class DomainEventAdapter : IDomainEventAdapter// where TFrom : DomainEvent where TTo : DomainEvent { public abstract IEnumerable ConvertEvent(TFrom evt); diff --git a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs b/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs new file mode 100644 index 00000000..840780f2 --- /dev/null +++ b/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json; + +namespace GridDomain.EventSourcing.DomainEventAdapters +{ + public static class DomainEventSerialization + { + public static JsonSerializerSettings GetDefault() + { + return new JsonSerializerSettings + { + Formatting = Formatting.Indented, + PreserveReferencesHandling = PreserveReferencesHandling.All, + TypeNameHandling = TypeNameHandling.Auto, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, + CheckAdditionalContent = true + }; + } + + //static DomainEventSerialization() + //{ + // var settings = new JsonSerializerSettings + // { + // Formatting = Formatting.Indented, + // PreserveReferencesHandling = PreserveReferencesHandling.All, + // TypeNameHandling = TypeNameHandling.Auto, + // TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, + // CheckAdditionalContent = true + // }; + //} + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs b/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs index 6a0cc8e9..8315856e 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs +++ b/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs @@ -3,8 +3,8 @@ namespace GridDomain.EventSourcing.DomainEventAdapters { public interface IDomainEventAdapter: IEventAdapter - where TFrom:DomainEvent - where TTo: DomainEvent + //where TFrom:DomainEvent + // where TTo: DomainEvent { IEnumerable ConvertEvent(TFrom evt); } diff --git a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj index 9f1fc247..59d4135e 100644 --- a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj +++ b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj @@ -35,6 +35,10 @@ ..\packages\NEventStore.5.2.0\lib\net40\NEventStore.dll False + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + @@ -47,6 +51,7 @@ + diff --git a/GridDomain.EventSourcing/packages.config b/GridDomain.EventSourcing/packages.config index 84bf8982..cc1c51f4 100644 --- a/GridDomain.EventSourcing/packages.config +++ b/GridDomain.EventSourcing/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file From f38527858682260c51527acf8fa72ddba4b1246e Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 18:31:39 +0300 Subject: [PATCH 02/33] sub-property & collection items upgrade green tests for JSON-based event adapters --- .../AggregatePersistedHub_Infrastructure.cs | 1 - ...InstanceSagaPersistedHub_Infrastructure.cs | 1 - .../StateSagaPersistedHub_Infrastructure.cs | 1 - .../PersistenceHubOnChildActivity.cs | 1 - .../PersistentHub_children_lifetime_test.cs | 1 - ...estPersistentChildsRecycleConfiguration.cs | 2 +- .../BalanceChangedDomainEventAdapter1.cs | 2 +- .../Chain/DomainEventUpdater1.cs | 2 +- .../Chain/DomainEventUpdater2.cs | 2 +- .../Chain/DomainEventUpdater3.cs | 2 +- ...eChain_When_updating_events_split_Tests.cs | 2 +- ...eChain_When_updating_single_event_Tests.cs | 2 +- .../EventsUpgrade/Nested_property_upgrade.cs | 64 ++++++ .../Nested_property_upgrade_by_constructor.cs | 204 ++++++++++++++++++ .../SubObject_version_upgrade.cs | 101 +++------ .../VersionedTypeSerializationBinderTests.cs | 2 +- .../GridDomain.Tests.csproj | 2 + ...should_fill_saga_id_for_produced_faults.cs | 1 + ...Given_saga_When_handling_command_faults.cs | 1 + .../CantFindSofaException.cs | 2 +- .../HomeAggregate.cs | 3 +- .../HomeAggregateHandler.cs | 3 +- .../SoftwareProgrammingDomain/HomeCreated.cs | 2 +- .../Sagas/SoftwareProgrammingDomain/Slept.cs | 2 +- .../DomainEventAdapter.cs | 4 +- .../Adapters/DomainEventSerialization.cs | 67 ++++++ .../EventAdapterDescriptor.cs | 2 +- .../EventAdaptersCatalog.cs | 2 +- .../Adapters/IDomainEventAdapter.cs | 11 + .../IEventAdapter.cs | 2 +- .../Adapters/ObjectAdapter.cs | 77 +++++++ .../Adapters/TypeNameNotFoundException.cs | 8 + .../VersionedTypeSerializationBinder.cs | 2 +- .../DomainEventSerialization.cs | 32 --- .../IDomainEventAdapter.cs | 11 - .../GridDomain.EventSourcing.csproj | 16 +- GridDomain.Node/AkkaDomainEventsAdapter.cs | 4 +- GridDomain.Node/DomainEventsSerializer.cs | 52 +++++ GridDomain.Node/GridDomain.Node.csproj | 1 + GridDomain.Node/GridDomainNode.cs | 2 +- .../Repositories/AggregateRepository.cs | 2 +- .../Repositories/DomainEventsRepository.cs | 4 +- .../RawSqlAkkaPersistenceRepository.cs | 4 +- 43 files changed, 550 insertions(+), 159 deletions(-) create mode 100644 GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs create mode 100644 GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs rename GridDomain.EventSourcing/{DomainEventAdapters => Adapters}/DomainEventAdapter.cs (79%) create mode 100644 GridDomain.EventSourcing/Adapters/DomainEventSerialization.cs rename GridDomain.EventSourcing/{DomainEventAdapters => Adapters}/EventAdapterDescriptor.cs (88%) rename GridDomain.EventSourcing/{DomainEventAdapters => Adapters}/EventAdaptersCatalog.cs (95%) create mode 100644 GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs rename GridDomain.EventSourcing/{DomainEventAdapters => Adapters}/IEventAdapter.cs (94%) create mode 100644 GridDomain.EventSourcing/Adapters/ObjectAdapter.cs create mode 100644 GridDomain.EventSourcing/Adapters/TypeNameNotFoundException.cs rename GridDomain.EventSourcing/{DomainEventAdapters => Adapters}/VersionedTypeSerializationBinder.cs (96%) delete mode 100644 GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs delete mode 100644 GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs create mode 100644 GridDomain.Node/DomainEventsSerializer.cs diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/AggregatePersistedHub_Infrastructure.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/AggregatePersistedHub_Infrastructure.cs index 33e54a81..5ca6abc9 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/AggregatePersistedHub_Infrastructure.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/AggregatePersistedHub_Infrastructure.cs @@ -2,7 +2,6 @@ using Akka.Actor; using Akka.DI.Core; using GridDomain.Node.Actors; -using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors; using GridDomain.Tests.SampleDomain; using GridDomain.Tests.SampleDomain.Commands; diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/InstanceSagaPersistedHub_Infrastructure.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/InstanceSagaPersistedHub_Infrastructure.cs index 0120c61d..f6f4c14e 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/InstanceSagaPersistedHub_Infrastructure.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/InstanceSagaPersistedHub_Infrastructure.cs @@ -4,7 +4,6 @@ using GridDomain.EventSourcing.Sagas; using GridDomain.EventSourcing.Sagas.InstanceSagas; using GridDomain.Node.Actors; -using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors; using GridDomain.Tests.Sagas.InstanceSagas; using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Events; diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/StateSagaPersistedHub_Infrastructure.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/StateSagaPersistedHub_Infrastructure.cs index e9e8b577..e6dd8028 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/StateSagaPersistedHub_Infrastructure.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/Infrastructure/StateSagaPersistedHub_Infrastructure.cs @@ -2,7 +2,6 @@ using Akka.Actor; using Akka.DI.Core; using GridDomain.Node.Actors; -using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors; using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Events; using GridDomain.Tests.Sagas.StateSagas.SampleSaga; diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistenceHubOnChildActivity.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistenceHubOnChildActivity.cs index 04261057..3a131638 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistenceHubOnChildActivity.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistenceHubOnChildActivity.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using Akka.Actor; -using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors; using NUnit.Framework; namespace GridDomain.Tests.Aggregate_Sagas_actor_lifetime diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistentHub_children_lifetime_test.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistentHub_children_lifetime_test.cs index 33cfe5bc..870350df 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistentHub_children_lifetime_test.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/PersistentHub_children_lifetime_test.cs @@ -10,7 +10,6 @@ using GridDomain.Node.Actors; using GridDomain.Node.Configuration.Composition; using GridDomain.Node.Configuration.Persistence; -using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors; using GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Infrastructure; using GridDomain.Tests.Framework.Configuration; using GridDomain.Tests.Sagas.InstanceSagas; diff --git a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/TestPersistentChildsRecycleConfiguration.cs b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/TestPersistentChildsRecycleConfiguration.cs index 330592ec..3bd7e42a 100644 --- a/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/TestPersistentChildsRecycleConfiguration.cs +++ b/GridDomain.Domain.Tests/Aggregate_Sagas_actor_lifetime/TestPersistentChildsRecycleConfiguration.cs @@ -1,7 +1,7 @@ using System; using GridDomain.Node.Actors; -namespace GridDomain.Tests.Aggregate_Sagas_actor_lifetime.Actors +namespace GridDomain.Tests.Aggregate_Sagas_actor_lifetime { internal class TestPersistentChildsRecycleConfiguration : IPersistentChildsRecycleConfiguration { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/BalanceChangedDomainEventAdapter1.cs b/GridDomain.Domain.Tests/EventsUpgrade/BalanceChangedDomainEventAdapter1.cs index 0135c27a..f911d579 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/BalanceChangedDomainEventAdapter1.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/BalanceChangedDomainEventAdapter1.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Domain.Events; namespace GridDomain.Tests.EventsUpgrade diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater1.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater1.cs index bea18bea..fe806650 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater1.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater1.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Events; namespace GridDomain.Tests.EventsUpgrade.Chain diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater2.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater2.cs index e24e95aa..2c143d09 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater2.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater2.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Events; namespace GridDomain.Tests.EventsUpgrade.Chain diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater3.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater3.cs index 1a85ac9b..ad852bc2 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater3.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/DomainEventUpdater3.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Events; namespace GridDomain.Tests.EventsUpgrade.Chain diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs index ad1ba98a..0fe09d27 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Domain; using GridDomain.Tests.EventsUpgrade.Events; using NUnit.Framework; diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs index 55f5ac0d..dc54dedc 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.Tests.EventsUpgrade.Domain; using GridDomain.Tests.EventsUpgrade.Events; using NUnit.Framework; diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs new file mode 100644 index 00000000..5f85c9c1 --- /dev/null +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs @@ -0,0 +1,64 @@ +using GridDomain.EventSourcing.Adapters; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace GridDomain.Tests.EventsUpgrade +{ + //[TestFixture] + //class Collection_items_property_upgrade + //{ + //} + + + [TestFixture] + class Nested_property_upgrade + { + [Test] + public void All_old_objects_should_be_updated() + { + var initialEvent = new Event() { Payload = new Payload() {Property = new SubObject_V1() { Name = "10", Value = "123" } }}; + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload.Property); + } + + class SubObjectConverter : ObjectAdapter + { + protected override SubObject_V2 Convert(SubObject_V1 value) + { + return new SubObject_V2() { number = int.Parse(value.Name), Value = value.Value }; + } + } + + private interface ISubObject + { + string Value { get; set; } + } + + class SubObject_V1 : ISubObject + { + public string Name { get; set; } + public string Value { get; set; } + } + + class SubObject_V2 : ISubObject + { + public int number { get; set; } + public string Value { get; set; } + } + + class Payload + { + public ISubObject Property { get; set; } + } + class Event + { + public Payload Payload { get; set; } + } + } +} \ No newline at end of file diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs new file mode 100644 index 00000000..a5ae9816 --- /dev/null +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs @@ -0,0 +1,204 @@ +using System; +using System.Linq; +using GridDomain.EventSourcing.Adapters; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace GridDomain.Tests.EventsUpgrade +{ + + [TestFixture] + class Collection_nested_property_upgrade_by_constructor + { + + [Test] + public void All_occurance_should_be_upgraded() + { + var initialEvent = new Event(new []{ new Payload(new ISubObject[]{new SubObject_V1("10", "123")})}); + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + Console.WriteLine(serializedValue); + + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); + } + + [Test] + + public void All_occurance_should_be_upgraded_with_implicit_collection_set() + { + //Should get an exception due to different serialized value + var initialEvent = new Event(new[] { new Payload(new [] { new SubObject_V1("10", "123") }) }); + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + Console.WriteLine(serializedValue); + Assert.Throws(() => + JsonConvert.DeserializeObject(serializedValue, settings)); + + } + + + [Test] + public void Collections_should_be_deserialized() + { + var initialEvent = new Event(new[] { new Payload(new[] { new SubObject_V1("10", "123") }) }); + + var settings = DomainEventSerialization.GetDefault(); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); + } + + + + class SubObjectConverter : ObjectAdapter + { + protected override SubObject_V2 Convert(SubObject_V1 value) + { + return new SubObject_V2(int.Parse(value.Name), value.Value); + } + } + + private interface ISubObject + { + string Value { get; } + } + + class SubObject_V1 : ISubObject + { + public SubObject_V1(string name, string value) + { + Name = name; + Value = value; + } + public string Name { get; } + public string Value { get; } + } + + class SubObject_V2 : ISubObject + { + + public SubObject_V2(int number, string value) + { + Number = number; + Value = value; + } + + public int Number { get; } + public string Value { get; } + } + + class Payload + { + public Payload(ISubObject[] property) + { + Property = property; + } + public ISubObject[] Property { get; } + } + class Event + { + public Event(Payload[] payload) + { + Payload = payload; + } + public Payload[] Payload { get; } + } + } + + [TestFixture] + class Nested_property_upgrade_by_constructor + { + + [Test] + public void All_occurance_should_be_upgraded() + { + var initialEvent = new Event(new Payload(new SubObject_V1("10", "123"))); + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload.Property); + } + + [Test] + public void Objects_with_custom_constructor_are_deserialized() + { + var initialEvent = new Event(new Payload(new SubObject_V1("10", "123"))); + + var settings = DomainEventSerialization.GetDefault(); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent?.Payload?.Property); + // Assert.AreEqual("10",(restoredEvent?.Payload?.Property as SubObject_V1)?.Name); + } + + class SubObjectConverter : ObjectAdapter + { + protected override SubObject_V2 Convert(SubObject_V1 value) + { + return new SubObject_V2( int.Parse(value.Name), value.Value); + } + } + + private interface ISubObject + { + string Value { get;} + } + + class SubObject_V1 : ISubObject + { + public SubObject_V1(string name, string value) + { + Name = name; + Value = value; + } + public string Name { get; } + public string Value { get; } + } + + class SubObject_V2 : ISubObject + { + + public SubObject_V2(int number, string value) + { + Number = number; + Value = value; + } + + public int Number { get; } + public string Value { get;} + } + + class Payload + { + public Payload(ISubObject property) + { + Property = property; + } + public ISubObject Property { get; } + } + class Event + { + public Event(Payload payload) + { + Payload = payload; + } + public Payload Payload { get;} + } + } +} \ No newline at end of file diff --git a/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs index ee4a2828..dd465a75 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs @@ -1,76 +1,18 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters; using System.Text; using System.Threading.Tasks; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; using NUnit.Framework; namespace GridDomain.Tests.EventsUpgrade { - abstract class ObjectConverter : JsonConverter - { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - - if (reader.Value != null && reader.ValueType == typeof(TFrom)) - { - TFrom convertFromValue = (TFrom)reader.Value; - return Convert(convertFromValue); - } - if (reader.TokenType == JsonToken.StartObject && existingValue == null) - { - var jObject = JObject.Load(reader); - - var type = PeekType(jObject); - if (type != typeof(TFrom)) - return serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); - - existingValue = serializer.ContractResolver.ResolveContract(type).DefaultCreator(); - serializer.Populate(jObject.CreateReader(), existingValue); - - return Convert((TFrom)existingValue); - } - if (reader.TokenType == JsonToken.Null) - { - return null; - } - throw new JsonSerializationException(); - } - - private static Type PeekType(JObject jobject) - { - string typeName; - if (string.IsNullOrEmpty(typeName = jobject["$type"]?.ToObject())) - throw new TypeNameNotFoundException(); - - var type = Type.GetType(typeName); - return type; - } - - protected abstract TTo Convert(TFrom value); - - public override bool CanWrite => false; - public override bool CanRead => true; - - public override bool CanConvert(Type objectType) - { - return typeof(TDeclared) == objectType; - } - } - [TestFixture] - class SubObject_version_upgrade + class Upgrade_object_in_property { private interface ISubObject { @@ -89,12 +31,12 @@ class SubObject_V2 : ISubObject public string Value { get; set; } } - class OldEvent + class Event { public ISubObject Payload { get; set; } } - class SubObjectCoverter : ObjectConverter + class SubObjectConverter : ObjectAdapter { protected override SubObject_V2 Convert(SubObject_V1 value) { @@ -105,42 +47,53 @@ protected override SubObject_V2 Convert(SubObject_V1 value) [Test] - public void Given_persisted_event_with_old_subobject_It_can_be_updated_to_new_subobject() + public void Property_upgraded_from_serialized_value() { var settings = DomainEventSerialization.GetDefault(); - settings.Converters.Add(new SubObjectCoverter()); + settings.Converters.Add(new SubObjectConverter()); var serializedValue = @"{ ""$id"": ""1"", ""Payload"": { ""$id"": ""2"", - ""$type"": ""GridDomain.Tests.EventsUpgrade.SubObject_version_upgrade+SubObject_V1, GridDomain.Tests"", + ""$type"": """+ typeof(SubObject_V1).AssemblyQualifiedName + @""", ""Name"": ""10"", ""Value"": ""123"" } }"; - var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); Assert.IsInstanceOf(restoredEvent.Payload); } [Test] - public void Given_event_with_old_subobject_it_Should_be_updated_to_new_subobject_by_subobject_adapter() + public void Propert_is_upgraded() { - var initialEvent = new OldEvent() {Payload = new SubObject_V1() {Name = "10",Value = "123"} }; + var initialEvent = new Event() {Payload = new SubObject_V1() {Name = "10",Value = "123"} }; var settings = DomainEventSerialization.GetDefault(); - settings.Converters.Add(new SubObjectCoverter()); + settings.Converters.Add(new SubObjectConverter()); var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); - var restoredEvent = JsonConvert.DeserializeObject(serializedValue,settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); Assert.IsInstanceOf(restoredEvent.Payload); } - } - internal class TypeNameNotFoundException : Exception - { + //[Test] + //public void Root_object_also_Should_be_converted() + //{ + // var initialEvent = new SubObject_V1() {Name = "10", Value = "123"}; + // + // var settings = DomainEventSerialization.GetDefault(); + // settings.Converters.Add(new SubObjectConverter()); + // + // var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + // + // var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + // + // Assert.IsInstanceOf(restoredEvent); + //} } } diff --git a/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeSerializationBinderTests.cs b/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeSerializationBinderTests.cs index ea6651ad..830e4cce 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeSerializationBinderTests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeSerializationBinderTests.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.EventSourcing.VersionedTypeSerialization; using GridDomain.Tests.EventsUpgrade.Events; using NUnit.Framework; diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index d6f94a54..7b19cf05 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -288,6 +288,8 @@ + + diff --git a/GridDomain.Domain.Tests/Sagas/InstanceSagas/Actors_should_fill_saga_id_for_produced_faults.cs b/GridDomain.Domain.Tests/Sagas/InstanceSagas/Actors_should_fill_saga_id_for_produced_faults.cs index 6bc48661..b415c4a0 100644 --- a/GridDomain.Domain.Tests/Sagas/InstanceSagas/Actors_should_fill_saga_id_for_produced_faults.cs +++ b/GridDomain.Domain.Tests/Sagas/InstanceSagas/Actors_should_fill_saga_id_for_produced_faults.cs @@ -7,6 +7,7 @@ using GridDomain.Node.Actors; using GridDomain.Node.AkkaMessaging; using GridDomain.Scheduling.Akka.Messages; +using GridDomain.Tests.Sagas.SoftwareProgrammingDomain; using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands; using GridDomain.Tests.SampleDomain.Events; using GridDomain.Tests.SampleDomain.ProjectionBuilders; diff --git a/GridDomain.Domain.Tests/Sagas/InstanceSagas/Given_saga_When_handling_command_faults.cs b/GridDomain.Domain.Tests/Sagas/InstanceSagas/Given_saga_When_handling_command_faults.cs index 867ec52f..1bbed5a4 100644 --- a/GridDomain.Domain.Tests/Sagas/InstanceSagas/Given_saga_When_handling_command_faults.cs +++ b/GridDomain.Domain.Tests/Sagas/InstanceSagas/Given_saga_When_handling_command_faults.cs @@ -7,6 +7,7 @@ using GridDomain.EventSourcing.Sagas; using GridDomain.EventSourcing.Sagas.InstanceSagas; using GridDomain.Node.Configuration.Composition; +using GridDomain.Tests.Sagas.SoftwareProgrammingDomain; using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands; using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Events; using NUnit.Framework; diff --git a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/CantFindSofaException.cs b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/CantFindSofaException.cs index 584bf50f..a0f5f671 100644 --- a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/CantFindSofaException.cs +++ b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/CantFindSofaException.cs @@ -1,6 +1,6 @@ using System; -namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands +namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain { public class CantFindSofaException : Exception { diff --git a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregate.cs b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregate.cs index 4ee6c1c5..90d2f9b2 100644 --- a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregate.cs +++ b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregate.cs @@ -1,8 +1,7 @@ using System; -using GridDomain.EventSourcing; using GridDomain.EventSourcing.Sagas.FutureEvents; -namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands +namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain { public class HomeAggregate : Aggregate { diff --git a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregateHandler.cs b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregateHandler.cs index ee8adcf2..24040836 100644 --- a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregateHandler.cs +++ b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeAggregateHandler.cs @@ -1,7 +1,8 @@ using System; using GridDomain.CQRS.Messaging.MessageRouting; +using GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands; -namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands +namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain { public class HomeAggregateHandler : AggregateCommandsHandler, IAggregateCommandsHandlerDesriptor { diff --git a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeCreated.cs b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeCreated.cs index c99b7414..02e3cb33 100644 --- a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeCreated.cs +++ b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/HomeCreated.cs @@ -1,7 +1,7 @@ using System; using GridDomain.EventSourcing; -namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands +namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain { public class HomeCreated : DomainEvent { diff --git a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/Slept.cs b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/Slept.cs index e74d2165..56c02177 100644 --- a/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/Slept.cs +++ b/GridDomain.Domain.Tests/Sagas/SoftwareProgrammingDomain/Slept.cs @@ -1,7 +1,7 @@ using System; using GridDomain.EventSourcing; -namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain.Commands +namespace GridDomain.Tests.Sagas.SoftwareProgrammingDomain { public class Slept : DomainEvent { diff --git a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs b/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs similarity index 79% rename from GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs rename to GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs index ccbc9360..e0913291 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; -namespace GridDomain.EventSourcing.DomainEventAdapters +namespace GridDomain.EventSourcing.Adapters { - public abstract class DomainEventAdapter : IDomainEventAdapter// where TFrom : DomainEvent where TTo : DomainEvent + public abstract class DomainEventAdapter : IDomainEventAdapter where TFrom : DomainEvent where TTo : DomainEvent { public abstract IEnumerable ConvertEvent(TFrom evt); diff --git a/GridDomain.EventSourcing/Adapters/DomainEventSerialization.cs b/GridDomain.EventSourcing/Adapters/DomainEventSerialization.cs new file mode 100644 index 00000000..fbef5a12 --- /dev/null +++ b/GridDomain.EventSourcing/Adapters/DomainEventSerialization.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace GridDomain.EventSourcing.Adapters +{ + public static class DomainEventSerialization + { + sealed class MostSpecifiedContructorResolver : DefaultContractResolver + { + + protected override JsonObjectContract CreateObjectContract(Type objectType) + { + var contract = base.CreateObjectContract(objectType); + if (!IsCustomStruct(objectType)) return contract; + + IList list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList(); + var mostSpecific = list.LastOrDefault(); + if (mostSpecific == null) return contract; + + contract.OverrideCreator = CreateParameterizedConstructor(mostSpecific); + var constructorParameters = CreateConstructorParameters(mostSpecific, contract.Properties); + foreach(var p in constructorParameters) + contract.CreatorParameters.Add(p); + + return contract; + } + + protected bool IsCustomStruct(Type objectType) + { + return objectType.IsValueType && + !objectType.IsPrimitive && + !objectType.IsEnum && + !string.IsNullOrEmpty(objectType.Namespace) && + !objectType.Namespace.StartsWith("System."); + } + + private ObjectConstructor CreateParameterizedConstructor(MethodBase method) + { + if(method == null) throw new ArgumentNullException(nameof(method)); + + var c = method as ConstructorInfo; + if (c != null) + return a => c.Invoke(a); + return a => method.Invoke(null, a); + } + } + public static JsonSerializerSettings GetDefault() + { + return new JsonSerializerSettings + { + Formatting = Formatting.Indented, + PreserveReferencesHandling = PreserveReferencesHandling.All, + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, + CheckAdditionalContent = false, + // ContractResolver = new MostSpecifiedContructorResolver(), + //ConstructorHandling = ConstructorHandling.Default + + }; + } + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/DomainEventAdapters/EventAdapterDescriptor.cs b/GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs similarity index 88% rename from GridDomain.EventSourcing/DomainEventAdapters/EventAdapterDescriptor.cs rename to GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs index 0d9c0db3..5e5368d7 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/EventAdapterDescriptor.cs +++ b/GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs @@ -1,6 +1,6 @@ using System; -namespace GridDomain.EventSourcing.DomainEventAdapters +namespace GridDomain.EventSourcing.Adapters { public class EventAdapterDescriptor { diff --git a/GridDomain.EventSourcing/DomainEventAdapters/EventAdaptersCatalog.cs b/GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs similarity index 95% rename from GridDomain.EventSourcing/DomainEventAdapters/EventAdaptersCatalog.cs rename to GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs index 292051a2..5d8c81c3 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/EventAdaptersCatalog.cs +++ b/GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace GridDomain.EventSourcing.DomainEventAdapters +namespace GridDomain.EventSourcing.Adapters { public class EventAdaptersCatalog { diff --git a/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs new file mode 100644 index 00000000..9523c414 --- /dev/null +++ b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace GridDomain.EventSourcing.Adapters +{ + public interface IDomainEventAdapter: IEventAdapter + where TFrom:DomainEvent + where TTo: DomainEvent + { + IEnumerable ConvertEvent(TFrom evt); + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/DomainEventAdapters/IEventAdapter.cs b/GridDomain.EventSourcing/Adapters/IEventAdapter.cs similarity index 94% rename from GridDomain.EventSourcing/DomainEventAdapters/IEventAdapter.cs rename to GridDomain.EventSourcing/Adapters/IEventAdapter.cs index 67a2f5d4..4f5178fc 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/IEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/IEventAdapter.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace GridDomain.EventSourcing.DomainEventAdapters +namespace GridDomain.EventSourcing.Adapters { /// /// How to updata and event diff --git a/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs new file mode 100644 index 00000000..4fe7549a --- /dev/null +++ b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs @@ -0,0 +1,77 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace GridDomain.EventSourcing.Adapters +{ + /// + /// Used to adapt objects from old versions to new one. + /// Support sub-property adaptation. + /// + /// + /// + /// + public abstract class ObjectAdapter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.Value != null && reader.ValueType == typeof(TFrom)) + { + TFrom convertFromValue = (TFrom)reader.Value; + return Convert(convertFromValue); + } + if (reader.TokenType == JsonToken.StartObject && existingValue == null) + { + object value; + var removed = serializer.Converters.Remove(this); + try + { + // Kludge to prevent infinite recursion when using JsonConverterAttribute on the type: deserialize to object. + value = serializer.Deserialize(reader); + } + finally + { + if (removed) serializer.Converters.Add(this); + } + + if (value is TFrom) + return Convert((TFrom) value); + + return value; + + } + if (reader.TokenType == JsonToken.Null) + { + return null; + } + throw new JsonSerializationException(); + } + + //private static Type PeekType(JObject jobject) + //{ + // string typeName; + // if (string.IsNullOrEmpty(typeName = jobject["$type"]?.ToObject())) + // throw new TypeNameNotFoundException(); + // + // var type = Type.GetType(typeName); + // return type; + //} + + protected abstract TTo Convert(TFrom value); + + public override bool CanWrite => false; + public override bool CanRead => true; + + public override bool CanConvert(Type objectType) + { + return typeof(TDeclared) == objectType || + typeof(TFrom) == objectType; + } + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/TypeNameNotFoundException.cs b/GridDomain.EventSourcing/Adapters/TypeNameNotFoundException.cs new file mode 100644 index 00000000..b5b0462b --- /dev/null +++ b/GridDomain.EventSourcing/Adapters/TypeNameNotFoundException.cs @@ -0,0 +1,8 @@ +using System; + +namespace GridDomain.EventSourcing.Adapters +{ + internal class TypeNameNotFoundException : Exception + { + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/DomainEventAdapters/VersionedTypeSerializationBinder.cs b/GridDomain.EventSourcing/Adapters/VersionedTypeSerializationBinder.cs similarity index 96% rename from GridDomain.EventSourcing/DomainEventAdapters/VersionedTypeSerializationBinder.cs rename to GridDomain.EventSourcing/Adapters/VersionedTypeSerializationBinder.cs index 78869434..e510bc75 100644 --- a/GridDomain.EventSourcing/DomainEventAdapters/VersionedTypeSerializationBinder.cs +++ b/GridDomain.EventSourcing/Adapters/VersionedTypeSerializationBinder.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; using GridDomain.EventSourcing.VersionedTypeSerialization; -namespace GridDomain.EventSourcing.DomainEventAdapters +namespace GridDomain.EventSourcing.Adapters { public class VersionedTypeSerializationBinder : SerializationBinder { diff --git a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs b/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs deleted file mode 100644 index 840780f2..00000000 --- a/GridDomain.EventSourcing/DomainEventAdapters/DomainEventSerialization.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Runtime.Serialization.Formatters; -using Newtonsoft.Json; - -namespace GridDomain.EventSourcing.DomainEventAdapters -{ - public static class DomainEventSerialization - { - public static JsonSerializerSettings GetDefault() - { - return new JsonSerializerSettings - { - Formatting = Formatting.Indented, - PreserveReferencesHandling = PreserveReferencesHandling.All, - TypeNameHandling = TypeNameHandling.Auto, - TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, - CheckAdditionalContent = true - }; - } - - //static DomainEventSerialization() - //{ - // var settings = new JsonSerializerSettings - // { - // Formatting = Formatting.Indented, - // PreserveReferencesHandling = PreserveReferencesHandling.All, - // TypeNameHandling = TypeNameHandling.Auto, - // TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, - // CheckAdditionalContent = true - // }; - //} - } -} \ No newline at end of file diff --git a/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs b/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs deleted file mode 100644 index 8315856e..00000000 --- a/GridDomain.EventSourcing/DomainEventAdapters/IDomainEventAdapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace GridDomain.EventSourcing.DomainEventAdapters -{ - public interface IDomainEventAdapter: IEventAdapter - //where TFrom:DomainEvent - // where TTo: DomainEvent - { - IEnumerable ConvertEvent(TFrom evt); - } -} \ No newline at end of file diff --git a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj index 59d4135e..b14eff08 100644 --- a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj +++ b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj @@ -51,15 +51,17 @@ - - - - - - - + + + + + + + + + diff --git a/GridDomain.Node/AkkaDomainEventsAdapter.cs b/GridDomain.Node/AkkaDomainEventsAdapter.cs index 818c4784..9797de38 100644 --- a/GridDomain.Node/AkkaDomainEventsAdapter.cs +++ b/GridDomain.Node/AkkaDomainEventsAdapter.cs @@ -1,14 +1,12 @@ -using System; using Akka.Actor; using Akka.Persistence.Journal; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.EventSourcing.VersionedTypeSerialization; using GridDomain.Node.Configuration.Akka.Hocon; using IEventAdapter = Akka.Persistence.Journal.IEventAdapter; namespace GridDomain.Node { - /// /// How to update an event /// 1) Create a copy of event and add existing number in type by convention _V(N) where N is version diff --git a/GridDomain.Node/DomainEventsSerializer.cs b/GridDomain.Node/DomainEventsSerializer.cs new file mode 100644 index 00000000..913f7761 --- /dev/null +++ b/GridDomain.Node/DomainEventsSerializer.cs @@ -0,0 +1,52 @@ +using System; +using Akka.Actor; +using Akka.Serialization; + +namespace GridDomain.Node +{ + public class DomainEventsSerializer : Serializer + { + public DomainEventsSerializer(ExtendedActorSystem system) : base(system) + { + } + + /// + /// Determines whether the deserializer needs a type hint to deserialize + /// an object. + /// + public override bool IncludeManifest => true; + + /// + /// Completely unique value to identify this implementation of the + /// used to optimize network traffic + /// + public override int Identifier => 21; + + + // + // Serializes the given object into a byte array + // + /// The object to serialize + /// A byte array containing the serialized object + public override byte[] ToBinary(object obj) + { + // Put the code that serializes the object here + // ... ... + return null; + } + + /// + /// Deserializes a byte array into an object using the type hint + // (if any, see "IncludeManifest" above) + /// + /// The array containing the serialized object + /// The type hint of the object contained in the array + /// The object contained in the array + public override object FromBinary(byte[] bytes, Type type) + { + // Put your code that deserializes here + // ... ... + return null; + } + } +} \ No newline at end of file diff --git a/GridDomain.Node/GridDomain.Node.csproj b/GridDomain.Node/GridDomain.Node.csproj index e39393c7..542a629a 100644 --- a/GridDomain.Node/GridDomain.Node.csproj +++ b/GridDomain.Node/GridDomain.Node.csproj @@ -297,6 +297,7 @@ + diff --git a/GridDomain.Node/GridDomainNode.cs b/GridDomain.Node/GridDomainNode.cs index f4362744..3bda5b26 100644 --- a/GridDomain.Node/GridDomainNode.cs +++ b/GridDomain.Node/GridDomainNode.cs @@ -14,7 +14,7 @@ using GridDomain.CQRS; using GridDomain.CQRS.Messaging; using GridDomain.CQRS.Messaging.Akka; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.EventSourcing.VersionedTypeSerialization; using GridDomain.Logging; using GridDomain.Node.Actors; diff --git a/GridDomain.Tools/Repositories/AggregateRepository.cs b/GridDomain.Tools/Repositories/AggregateRepository.cs index 99f224df..53ac0bdd 100644 --- a/GridDomain.Tools/Repositories/AggregateRepository.cs +++ b/GridDomain.Tools/Repositories/AggregateRepository.cs @@ -3,7 +3,7 @@ using CommonDomain; using CommonDomain.Core; using GridDomain.EventSourcing; -using GridDomain.EventSourcing.DomainEventAdapters; +using GridDomain.EventSourcing.Adapters; using GridDomain.EventSourcing.Sagas.FutureEvents; using GridDomain.Node.AkkaMessaging; using GridDomain.Tools.Persistence; diff --git a/GridDomain.Tools/Repositories/DomainEventsRepository.cs b/GridDomain.Tools/Repositories/DomainEventsRepository.cs index 55e53ef6..3b7e5658 100644 --- a/GridDomain.Tools/Repositories/DomainEventsRepository.cs +++ b/GridDomain.Tools/Repositories/DomainEventsRepository.cs @@ -3,11 +3,11 @@ using GridDomain.EventSourcing; using GridDomain.Node.Configuration.Akka.Hocon; using GridDomain.Tools.Persistence.SqlPersistence; -using GridDomain.Tools.Repositories; using Wire; + //using Wire.Extensions; -namespace GridDomain.Tools.Persistence +namespace GridDomain.Tools.Repositories { public class DomainEventsRepository : IRepository { diff --git a/GridDomain.Tools/Repositories/RawSqlAkkaPersistenceRepository.cs b/GridDomain.Tools/Repositories/RawSqlAkkaPersistenceRepository.cs index 6dd49f93..2d7b7dfb 100644 --- a/GridDomain.Tools/Repositories/RawSqlAkkaPersistenceRepository.cs +++ b/GridDomain.Tools/Repositories/RawSqlAkkaPersistenceRepository.cs @@ -1,10 +1,8 @@ -using System; using System.Data.Entity.Migrations; using System.Linq; using GridDomain.Tools.Persistence.SqlPersistence; -using GridDomain.Tools.Repositories; -namespace GridDomain.Tools.Persistence +namespace GridDomain.Tools.Repositories { /// /// Class for reading \ writing data persisted in sql db with wire From 32976539003707ffcfd26548a094dc7bd228686d Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 18:33:13 +0300 Subject: [PATCH 03/33] Moved classes to files --- ..._nested_property_upgrade_by_constructor.cs | 116 ++++++++++++++++++ .../EventsUpgrade/Nested_property_upgrade.cs | 6 - ...grade.cs => Upgrade_object_in_property.cs} | 0 3 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs rename GridDomain.Domain.Tests/EventsUpgrade/{SubObject_version_upgrade.cs => Upgrade_object_in_property.cs} (100%) diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs new file mode 100644 index 00000000..68b0b942 --- /dev/null +++ b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs @@ -0,0 +1,116 @@ +using System; +using System.Linq; +using GridDomain.EventSourcing.Adapters; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace GridDomain.Tests.EventsUpgrade +{ + [TestFixture] + class Collection_nested_property_upgrade_by_constructor + { + + [Test] + public void All_occurance_should_be_upgraded() + { + var initialEvent = new Event(new []{ new Payload(new ISubObject[]{new SubObject_V1("10", "123")})}); + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + Console.WriteLine(serializedValue); + + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); + } + + [Test] + + public void All_occurance_should_be_upgraded_with_implicit_collection_set() + { + //Should get an exception due to different serialized value + var initialEvent = new Event(new[] { new Payload(new [] { new SubObject_V1("10", "123") }) }); + + var settings = DomainEventSerialization.GetDefault(); + settings.Converters.Add(new SubObjectConverter()); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + Console.WriteLine(serializedValue); + Assert.Throws(() => + JsonConvert.DeserializeObject(serializedValue, settings)); + + } + + + [Test] + public void Collections_should_be_deserialized() + { + var initialEvent = new Event(new[] { new Payload(new[] { new SubObject_V1("10", "123") }) }); + + var settings = DomainEventSerialization.GetDefault(); + + var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); + var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); + + Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); + } + + + + class SubObjectConverter : ObjectAdapter + { + protected override SubObject_V2 Convert(SubObject_V1 value) + { + return new SubObject_V2(int.Parse(value.Name), value.Value); + } + } + + private interface ISubObject + { + string Value { get; } + } + + class SubObject_V1 : ISubObject + { + public SubObject_V1(string name, string value) + { + Name = name; + Value = value; + } + public string Name { get; } + public string Value { get; } + } + + class SubObject_V2 : ISubObject + { + + public SubObject_V2(int number, string value) + { + Number = number; + Value = value; + } + + public int Number { get; } + public string Value { get; } + } + + class Payload + { + public Payload(ISubObject[] property) + { + Property = property; + } + public ISubObject[] Property { get; } + } + class Event + { + public Event(Payload[] payload) + { + Payload = payload; + } + public Payload[] Payload { get; } + } + } +} \ No newline at end of file diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs index 5f85c9c1..45d15b4a 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs @@ -4,12 +4,6 @@ namespace GridDomain.Tests.EventsUpgrade { - //[TestFixture] - //class Collection_items_property_upgrade - //{ - //} - - [TestFixture] class Nested_property_upgrade { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs similarity index 100% rename from GridDomain.Domain.Tests/EventsUpgrade/SubObject_version_upgrade.cs rename to GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs From 53f8ed1ab9bb6fb76f7e82c3e06a1bcf750fc5b7 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 18:41:40 +0300 Subject: [PATCH 04/33] Simplifying object adapter & code cleanup --- ..._nested_property_upgrade_by_constructor.cs | 2 +- .../EventsUpgrade/Nested_property_upgrade.cs | 2 +- .../Nested_property_upgrade_by_constructor.cs | 114 +----------------- .../Upgrade_object_in_property.cs | 2 +- .../GridDomain.Tests.csproj | 3 +- .../Adapters/IDomainEventAdapter.cs | 6 + .../Adapters/ObjectAdapter.cs | 35 +----- 7 files changed, 18 insertions(+), 146 deletions(-) diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs index 68b0b942..b3165296 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs @@ -59,7 +59,7 @@ public void Collections_should_be_deserialized() - class SubObjectConverter : ObjectAdapter + class SubObjectConverter : ObjectAdapter { protected override SubObject_V2 Convert(SubObject_V1 value) { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs index 45d15b4a..d220a9c1 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs @@ -21,7 +21,7 @@ public void All_old_objects_should_be_updated() Assert.IsInstanceOf(restoredEvent.Payload.Property); } - class SubObjectConverter : ObjectAdapter + class SubObjectConverter : ObjectAdapter { protected override SubObject_V2 Convert(SubObject_V1 value) { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs index a5ae9816..ff67d735 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs @@ -1,120 +1,9 @@ -using System; -using System.Linq; using GridDomain.EventSourcing.Adapters; using Newtonsoft.Json; using NUnit.Framework; namespace GridDomain.Tests.EventsUpgrade { - - [TestFixture] - class Collection_nested_property_upgrade_by_constructor - { - - [Test] - public void All_occurance_should_be_upgraded() - { - var initialEvent = new Event(new []{ new Payload(new ISubObject[]{new SubObject_V1("10", "123")})}); - - var settings = DomainEventSerialization.GetDefault(); - settings.Converters.Add(new SubObjectConverter()); - - var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); - Console.WriteLine(serializedValue); - - var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); - - Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); - } - - [Test] - - public void All_occurance_should_be_upgraded_with_implicit_collection_set() - { - //Should get an exception due to different serialized value - var initialEvent = new Event(new[] { new Payload(new [] { new SubObject_V1("10", "123") }) }); - - var settings = DomainEventSerialization.GetDefault(); - settings.Converters.Add(new SubObjectConverter()); - - var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); - Console.WriteLine(serializedValue); - Assert.Throws(() => - JsonConvert.DeserializeObject(serializedValue, settings)); - - } - - - [Test] - public void Collections_should_be_deserialized() - { - var initialEvent = new Event(new[] { new Payload(new[] { new SubObject_V1("10", "123") }) }); - - var settings = DomainEventSerialization.GetDefault(); - - var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); - var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); - - Assert.IsInstanceOf(restoredEvent.Payload?.FirstOrDefault()?.Property?.FirstOrDefault()); - } - - - - class SubObjectConverter : ObjectAdapter - { - protected override SubObject_V2 Convert(SubObject_V1 value) - { - return new SubObject_V2(int.Parse(value.Name), value.Value); - } - } - - private interface ISubObject - { - string Value { get; } - } - - class SubObject_V1 : ISubObject - { - public SubObject_V1(string name, string value) - { - Name = name; - Value = value; - } - public string Name { get; } - public string Value { get; } - } - - class SubObject_V2 : ISubObject - { - - public SubObject_V2(int number, string value) - { - Number = number; - Value = value; - } - - public int Number { get; } - public string Value { get; } - } - - class Payload - { - public Payload(ISubObject[] property) - { - Property = property; - } - public ISubObject[] Property { get; } - } - class Event - { - public Event(Payload[] payload) - { - Payload = payload; - } - public Payload[] Payload { get; } - } - } - [TestFixture] class Nested_property_upgrade_by_constructor { @@ -144,10 +33,9 @@ public void Objects_with_custom_constructor_are_deserialized() var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); Assert.IsInstanceOf(restoredEvent?.Payload?.Property); - // Assert.AreEqual("10",(restoredEvent?.Payload?.Property as SubObject_V1)?.Name); } - class SubObjectConverter : ObjectAdapter + class SubObjectConverter : ObjectAdapter { protected override SubObject_V2 Convert(SubObject_V1 value) { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs index dd465a75..acedaa74 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs @@ -36,7 +36,7 @@ class Event public ISubObject Payload { get; set; } } - class SubObjectConverter : ObjectAdapter + class SubObjectConverter : ObjectAdapter { protected override SubObject_V2 Convert(SubObject_V1 value) { diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 7b19cf05..423d4d2d 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -266,6 +266,7 @@ + @@ -290,7 +291,7 @@ - + diff --git a/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs index 9523c414..5adb033d 100644 --- a/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs @@ -8,4 +8,10 @@ public interface IDomainEventAdapter: IEventAdapter { IEnumerable ConvertEvent(TFrom evt); } + + public interface IObjectAdapter + { + TTo Convert(TFrom evt); + } + } \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs index 4fe7549a..5bc4e289 100644 --- a/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs @@ -7,12 +7,11 @@ namespace GridDomain.EventSourcing.Adapters { /// /// Used to adapt objects from old versions to new one. - /// Support sub-property adaptation. + /// Support sub-property & collection items adaptation. /// - /// /// /// - public abstract class ObjectAdapter : JsonConverter + public abstract class ObjectAdapter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { @@ -21,24 +20,13 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - if (reader.Value != null && reader.ValueType == typeof(TFrom)) - { - TFrom convertFromValue = (TFrom)reader.Value; - return Convert(convertFromValue); - } if (reader.TokenType == JsonToken.StartObject && existingValue == null) { object value; + //prevent infinite recursion var removed = serializer.Converters.Remove(this); - try - { - // Kludge to prevent infinite recursion when using JsonConverterAttribute on the type: deserialize to object. - value = serializer.Deserialize(reader); - } - finally - { - if (removed) serializer.Converters.Add(this); - } + try { value = serializer.Deserialize(reader); } + finally { if (removed) serializer.Converters.Add(this);} if (value is TFrom) return Convert((TFrom) value); @@ -53,16 +41,6 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist throw new JsonSerializationException(); } - //private static Type PeekType(JObject jobject) - //{ - // string typeName; - // if (string.IsNullOrEmpty(typeName = jobject["$type"]?.ToObject())) - // throw new TypeNameNotFoundException(); - // - // var type = Type.GetType(typeName); - // return type; - //} - protected abstract TTo Convert(TFrom value); public override bool CanWrite => false; @@ -70,8 +48,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist public override bool CanConvert(Type objectType) { - return typeof(TDeclared) == objectType || - typeof(TFrom) == objectType; + return objectType.IsAssignableFrom(typeof(TFrom)); } } } \ No newline at end of file From a458aff077acb85b46d15be2f68846a10b1e8694 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 18:46:00 +0300 Subject: [PATCH 05/33] test class rename --- ...or.cs => Nested_property_upgrade_with_custom_constructor.cs} | 2 +- GridDomain.Domain.Tests/GridDomain.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename GridDomain.Domain.Tests/EventsUpgrade/{Nested_property_upgrade_by_constructor.cs => Nested_property_upgrade_with_custom_constructor.cs} (97%) diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs similarity index 97% rename from GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs rename to GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs index ff67d735..46fd492f 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_by_constructor.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs @@ -5,7 +5,7 @@ namespace GridDomain.Tests.EventsUpgrade { [TestFixture] - class Nested_property_upgrade_by_constructor + class Nested_property_upgrade_with_custom_constructor { [Test] diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 423d4d2d..7784db3c 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -290,7 +290,7 @@ - + From 7a49a34b6666f272e16f81afe98da4494479d070 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 19:09:10 +0300 Subject: [PATCH 06/33] Simplified events upgrade chain algorithm Removed EventAdapterDescriptor class --- ...eChain_When_updating_events_split_Tests.cs | 2 +- ...eChain_When_updating_single_event_Tests.cs | 2 +- ..._nested_property_upgrade_by_constructor.cs | 2 +- ...gate_with_upgraded_event_with_new_field.cs | 2 +- .../EventsUpgrade/Nested_property_upgrade.cs | 2 +- ...roperty_upgrade_with_custom_constructor.cs | 2 +- .../Upgrade_object_in_property.cs | 2 +- .../Adapters/DomainEventAdapter.cs | 2 - .../Adapters/EventAdapterDescriptor.cs | 21 ---------- .../Adapters/EventAdaptersCatalog.cs | 40 ------------------- .../Adapters/EventsAdaptersCatalog.cs | 39 ++++++++++++++++++ .../Adapters/IDomainEventAdapter.cs | 23 ++++++++++- .../Adapters/IEventAdapter.cs | 2 +- .../Adapters/ObjectAdapter.cs | 10 ++++- .../GridDomain.EventSourcing.csproj | 3 +- GridDomain.Node/AkkaDomainEventsAdapter.cs | 2 +- GridDomain.Node/GridDomainNode.cs | 2 +- .../Repositories/AggregateRepository.cs | 12 +++--- 18 files changed, 86 insertions(+), 84 deletions(-) delete mode 100644 GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs delete mode 100644 GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs create mode 100644 GridDomain.EventSourcing/Adapters/EventsAdaptersCatalog.cs diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs index 0fe09d27..e5710370 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_events_split_Tests.cs @@ -17,7 +17,7 @@ public class Given_DomainEventUpgradeChain_When_updating_events_split_Tests [OneTimeSetUp] public void When_updating_single_event_Tests() { - var chain = new EventAdaptersCatalog(); + var chain = new EventsAdaptersCatalog(); chain.Register(new DomainEventUpdater3()); var balanceAggregate = new BalanceAggregate(Guid.NewGuid(), 10); _initialEvent = new TestEvent_V2(balanceAggregate.Id); diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs index dc54dedc..1a0e582c 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Chain/Given_DomainEventUpgradeChain_When_updating_single_event_Tests.cs @@ -16,7 +16,7 @@ public class Given_DomainEventUpgradeChain_When_updating_single_event_Tests [OneTimeSetUp] public void When_updating_single_event_Tests() { - var chain = new EventAdaptersCatalog(); + var chain = new EventsAdaptersCatalog(); chain.Register(new DomainEventUpdater1()); chain.Register(new DomainEventUpdater2()); diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs index b3165296..56bb5f2d 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Collection_nested_property_upgrade_by_constructor.cs @@ -61,7 +61,7 @@ public void Collections_should_be_deserialized() class SubObjectConverter : ObjectAdapter { - protected override SubObject_V2 Convert(SubObject_V1 value) + public override SubObject_V2 Convert(SubObject_V1 value) { return new SubObject_V2(int.Parse(value.Name), value.Value); } diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field.cs b/GridDomain.Domain.Tests/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field.cs index 0686d919..f62a02ee 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field.cs @@ -30,7 +30,7 @@ public class Given_aggregate_with_upgraded_event_with_new_field: ExtendedNodeCom [OneTimeSetUp] public void When_aggregate_is_recovered_from_persistence() { - GridNode.EventAdaptersCatalog.Register(new BalanceChangedDomainEventAdapter1()); + GridNode.EventsAdaptersCatalog.Register(new BalanceChangedDomainEventAdapter1()); _balanceId = Guid.NewGuid(); var events = new DomainEvent[] { diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs index d220a9c1..e9289022 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade.cs @@ -23,7 +23,7 @@ public void All_old_objects_should_be_updated() class SubObjectConverter : ObjectAdapter { - protected override SubObject_V2 Convert(SubObject_V1 value) + public override SubObject_V2 Convert(SubObject_V1 value) { return new SubObject_V2() { number = int.Parse(value.Name), Value = value.Value }; } diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs index 46fd492f..5030c508 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Nested_property_upgrade_with_custom_constructor.cs @@ -37,7 +37,7 @@ public void Objects_with_custom_constructor_are_deserialized() class SubObjectConverter : ObjectAdapter { - protected override SubObject_V2 Convert(SubObject_V1 value) + public override SubObject_V2 Convert(SubObject_V1 value) { return new SubObject_V2( int.Parse(value.Name), value.Value); } diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs index acedaa74..77bc0600 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs @@ -38,7 +38,7 @@ class Event class SubObjectConverter : ObjectAdapter { - protected override SubObject_V2 Convert(SubObject_V1 value) + public override SubObject_V2 Convert(SubObject_V1 value) { return new SubObject_V2() { number = int.Parse(value.Name), Value = value.Value }; } diff --git a/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs b/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs index e0913291..9aecdd4f 100644 --- a/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/DomainEventAdapter.cs @@ -7,8 +7,6 @@ public abstract class DomainEventAdapter : IDomainEventAdapter ConvertEvent(TFrom evt); - public EventAdapterDescriptor Descriptor { get; } = new EventAdapterDescriptor(typeof(TFrom), typeof(TTo)); - IEnumerable IEventAdapter.Convert(object evt) { IEnumerable updatedEvents = ConvertEvent((TFrom)evt); diff --git a/GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs b/GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs deleted file mode 100644 index 5e5368d7..00000000 --- a/GridDomain.EventSourcing/Adapters/EventAdapterDescriptor.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace GridDomain.EventSourcing.Adapters -{ - public class EventAdapterDescriptor - { - public EventAdapterDescriptor(Type @from, Type to) - { - From = @from; - To = to; - } - - public Type From { get; } - public Type To { get; } - - public static EventAdapterDescriptor New() - { - return new EventAdapterDescriptor(typeof(TFrom), typeof(TTo)); - } - } -} \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs b/GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs deleted file mode 100644 index 5d8c81c3..00000000 --- a/GridDomain.EventSourcing/Adapters/EventAdaptersCatalog.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GridDomain.EventSourcing.Adapters -{ - public class EventAdaptersCatalog - { - private readonly IDictionary _adapterCatalog = new Dictionary(); - - public object[] Update(object evt) - { - IEventAdapter adapter = null; - var processingType = evt.GetType(); - List updatedEvent = new List{evt}; - - while (_adapterCatalog.TryGetValue(processingType, out adapter)) - { - var eventsToProcess = updatedEvent.ToArray(); - processingType = adapter.Descriptor.To; - - updatedEvent.Clear(); - foreach(var ev in eventsToProcess) - updatedEvent.AddRange(adapter.Convert(ev)); - } - - return updatedEvent.ToArray(); - } - - /// - /// All types should form a chain - /// - /// - /// - /// - public void Register(IDomainEventAdapter adapter) where TFrom : DomainEvent where TTo : DomainEvent - { - _adapterCatalog[typeof(TFrom)] = adapter; - } - } -} \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/EventsAdaptersCatalog.cs b/GridDomain.EventSourcing/Adapters/EventsAdaptersCatalog.cs new file mode 100644 index 00000000..7f477199 --- /dev/null +++ b/GridDomain.EventSourcing/Adapters/EventsAdaptersCatalog.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GridDomain.EventSourcing.Adapters +{ + public class EventsAdaptersCatalog + { + private readonly IDictionary _eventAdapterCatalog = new Dictionary(); + private readonly IDictionary _objectAdapterCatalog = new Dictionary(); + + public object[] Update(object evt) + { + IEventAdapter adapter; + var processingType = evt.GetType(); + + if(!_eventAdapterCatalog.TryGetValue(processingType, out adapter)) return new[]{evt}; + + var updatedEvent = adapter.Convert(evt); + return updatedEvent.SelectMany(Update).ToArray(); + } + + /// + /// All types should form a chain + /// + /// + /// + /// + public void Register(IDomainEventAdapter adapter) where TFrom : DomainEvent where TTo : DomainEvent + { + _eventAdapterCatalog[typeof(TFrom)] = adapter; + } + + public void Register(IObjectAdapter adapter) + { + _objectAdapterCatalog[typeof(TFrom)] = adapter; + } + } +} \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs index 5adb033d..aef4e788 100644 --- a/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/IDomainEventAdapter.cs @@ -2,6 +2,13 @@ namespace GridDomain.EventSourcing.Adapters { + /// + /// Adapter used to change domain events in terms of new version, e.g : + /// field add, rename, delete or type change. + /// Works on events after their deserialization. + /// + /// + /// public interface IDomainEventAdapter: IEventAdapter where TFrom:DomainEvent where TTo: DomainEvent @@ -9,9 +16,23 @@ public interface IDomainEventAdapter: IEventAdapter IEnumerable ConvertEvent(TFrom evt); } - public interface IObjectAdapter + /// + /// Object adapter used for value object upgrade in domain events + /// If value object is changed, you should create one object adaptor for this + /// and none of domain event adapter. + /// Works as part of deserialiation logic. + /// Can be used on interface implementation or child classes change + /// + /// + /// + public interface IObjectAdapter : IObjectAdapter { TTo Convert(TFrom evt); } + public interface IObjectAdapter + { + object Convert(object evt); + } + } \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/IEventAdapter.cs b/GridDomain.EventSourcing/Adapters/IEventAdapter.cs index 4f5178fc..cc9c6792 100644 --- a/GridDomain.EventSourcing/Adapters/IEventAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/IEventAdapter.cs @@ -21,7 +21,7 @@ namespace GridDomain.EventSourcing.Adapters /// public interface IEventAdapter { - EventAdapterDescriptor Descriptor { get; } + //EventAdapterDescriptor Descriptor { get; } IEnumerable Convert(object evt); } } \ No newline at end of file diff --git a/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs index 5bc4e289..e0c6558d 100644 --- a/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs +++ b/GridDomain.EventSourcing/Adapters/ObjectAdapter.cs @@ -11,7 +11,8 @@ namespace GridDomain.EventSourcing.Adapters /// /// /// - public abstract class ObjectAdapter : JsonConverter + public abstract class ObjectAdapter : JsonConverter, + IObjectAdapter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { @@ -41,7 +42,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist throw new JsonSerializationException(); } - protected abstract TTo Convert(TFrom value); + public abstract TTo Convert(TFrom value); public override bool CanWrite => false; public override bool CanRead => true; @@ -50,5 +51,10 @@ public override bool CanConvert(Type objectType) { return objectType.IsAssignableFrom(typeof(TFrom)); } + + public object Convert(object evt) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj index b14eff08..3d19b6bb 100644 --- a/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj +++ b/GridDomain.EventSourcing/GridDomain.EventSourcing.csproj @@ -52,9 +52,8 @@ - - + diff --git a/GridDomain.Node/AkkaDomainEventsAdapter.cs b/GridDomain.Node/AkkaDomainEventsAdapter.cs index 9797de38..dea11b4a 100644 --- a/GridDomain.Node/AkkaDomainEventsAdapter.cs +++ b/GridDomain.Node/AkkaDomainEventsAdapter.cs @@ -27,7 +27,7 @@ namespace GridDomain.Node public class AkkaDomainEventsAdapter : IEventAdapter { - public static readonly EventAdaptersCatalog UpgradeChain = new EventAdaptersCatalog(); + public static readonly EventsAdaptersCatalog UpgradeChain = new EventsAdaptersCatalog(); private readonly ExtendedActorSystem _system; //always called first from Akka internals diff --git a/GridDomain.Node/GridDomainNode.cs b/GridDomain.Node/GridDomainNode.cs index 3bda5b26..83d5420f 100644 --- a/GridDomain.Node/GridDomainNode.cs +++ b/GridDomain.Node/GridDomainNode.cs @@ -181,7 +181,7 @@ private void ConfigureContainer(IUnityContainer unityContainer, bool _stopping = false; private NodeCommandExecutor _commandExecutor; - public EventAdaptersCatalog EventAdaptersCatalog { get; } = AkkaDomainEventsAdapter.UpgradeChain; + public EventsAdaptersCatalog EventsAdaptersCatalog { get; } = AkkaDomainEventsAdapter.UpgradeChain; public void Stop() { diff --git a/GridDomain.Tools/Repositories/AggregateRepository.cs b/GridDomain.Tools/Repositories/AggregateRepository.cs index 53ac0bdd..9bb392a1 100644 --- a/GridDomain.Tools/Repositories/AggregateRepository.cs +++ b/GridDomain.Tools/Repositories/AggregateRepository.cs @@ -13,11 +13,11 @@ namespace GridDomain.Tools.Repositories public class AggregateRepository : IDisposable { private readonly IRepository _eventRepository; - private readonly EventAdaptersCatalog _eventAdaptersCatalog; + private readonly EventsAdaptersCatalog _eventsAdaptersCatalog; - public AggregateRepository(IRepository eventRepository, EventAdaptersCatalog eventAdaptersCatalog = null) + public AggregateRepository(IRepository eventRepository, EventsAdaptersCatalog eventsAdaptersCatalog = null) { - _eventAdaptersCatalog = eventAdaptersCatalog ?? new EventAdaptersCatalog(); + _eventsAdaptersCatalog = eventsAdaptersCatalog ?? new EventsAdaptersCatalog(); _eventRepository = eventRepository; } @@ -33,7 +33,7 @@ public T LoadAggregate(Guid id) where T : AggregateBase var agr = Aggregate.Empty(id); var persistId = AggregateActorName.New(id).ToString(); var events = _eventRepository.Load(persistId); - foreach(var e in events.SelectMany(e => _eventAdaptersCatalog.Update(e))) + foreach(var e in events.SelectMany(e => _eventsAdaptersCatalog.Update(e))) ((IAggregate)agr).ApplyEvent(e); return agr; } @@ -42,11 +42,11 @@ public void Dispose() { } - public static AggregateRepository New(string akkaWriteDbConnectionString, EventAdaptersCatalog eventUpgradeCatalog = null) + public static AggregateRepository New(string akkaWriteDbConnectionString, EventsAdaptersCatalog upgradeCatalog = null) { var rawSqlAkkaPersistenceRepository = new RawSqlAkkaPersistenceRepository(akkaWriteDbConnectionString); var domainEventsRepository = new DomainEventsRepository(rawSqlAkkaPersistenceRepository); - return new AggregateRepository(domainEventsRepository, eventUpgradeCatalog); + return new AggregateRepository(domainEventsRepository, upgradeCatalog); } } } \ No newline at end of file From 6b338420fc751ec9612026f1ead0986ed3009f4d Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sat, 1 Oct 2016 19:55:54 +0300 Subject: [PATCH 07/33] Adding Json event adapter to GridDomain --- GridDomain.EventSourcing/DomainEvent.cs | 1 - GridDomain.Node/AkkaDomainEventsAdapter.cs | 2 - ...lizer.cs => DomainEventsJsonSerializer.cs} | 44 +++++++++++++++---- GridDomain.Node/GridDomain.Node.csproj | 2 +- GridDomain.Node/GridDomainNode.cs | 11 +++++ 5 files changed, 47 insertions(+), 13 deletions(-) rename GridDomain.Node/{DomainEventsSerializer.cs => DomainEventsJsonSerializer.cs} (52%) diff --git a/GridDomain.EventSourcing/DomainEvent.cs b/GridDomain.EventSourcing/DomainEvent.cs index 20ca6180..cd755bb2 100644 --- a/GridDomain.EventSourcing/DomainEvent.cs +++ b/GridDomain.EventSourcing/DomainEvent.cs @@ -7,7 +7,6 @@ namespace GridDomain.EventSourcing { - //[Serializable] public class DomainEvent : ISourcedEvent//, ISerializable { public DomainEvent(Guid sourceId, DateTime? createdTime = null, Guid sagaId = default(Guid)) diff --git a/GridDomain.Node/AkkaDomainEventsAdapter.cs b/GridDomain.Node/AkkaDomainEventsAdapter.cs index dea11b4a..6306e043 100644 --- a/GridDomain.Node/AkkaDomainEventsAdapter.cs +++ b/GridDomain.Node/AkkaDomainEventsAdapter.cs @@ -28,7 +28,6 @@ namespace GridDomain.Node public class AkkaDomainEventsAdapter : IEventAdapter { public static readonly EventsAdaptersCatalog UpgradeChain = new EventsAdaptersCatalog(); - private readonly ExtendedActorSystem _system; //always called first from Akka internals //if no constructor with system found, we would have en log polluted with confusing exception @@ -36,7 +35,6 @@ public class AkkaDomainEventsAdapter : IEventAdapter //https://github.com/akkadotnet/akka.net/blob/df5f6ebc9e7f6b92aef3ca6d63bb1ab365f1c8fa/src/core/Akka.Persistence/Journal/EventAdapters.cs#L336 public AkkaDomainEventsAdapter(ExtendedActorSystem system) { - _system = system; } public string Manifest(object evt) diff --git a/GridDomain.Node/DomainEventsSerializer.cs b/GridDomain.Node/DomainEventsJsonSerializer.cs similarity index 52% rename from GridDomain.Node/DomainEventsSerializer.cs rename to GridDomain.Node/DomainEventsJsonSerializer.cs index 913f7761..3897836b 100644 --- a/GridDomain.Node/DomainEventsSerializer.cs +++ b/GridDomain.Node/DomainEventsJsonSerializer.cs @@ -1,13 +1,36 @@ using System; +using System.Collections.Generic; +using System.IO; using Akka.Actor; using Akka.Serialization; +using GridDomain.EventSourcing.Adapters; +using Newtonsoft.Json; namespace GridDomain.Node { - public class DomainEventsSerializer : Serializer + + public class DomainEventsJsonSerializer : Serializer { - public DomainEventsSerializer(ExtendedActorSystem system) : base(system) + private JsonSerializer _serializer; + private readonly List _converters = new List(); + + + public DomainEventsJsonSerializer(ExtendedActorSystem system) : base(system) + { + Init(); + } + + public void Register(JsonConverter converter) { + _converters.Add(converter); + } + + public void Init() + { + var jsonSerializerSettings = DomainEventSerialization.GetDefault(); + foreach (var converter in _converters) + jsonSerializerSettings.Converters.Add(converter); + _serializer = JsonSerializer.Create(jsonSerializerSettings); } /// @@ -21,7 +44,7 @@ public DomainEventsSerializer(ExtendedActorSystem system) : base(system) /// used to optimize network traffic /// public override int Identifier => 21; - + // // Serializes the given object into a byte array @@ -30,9 +53,12 @@ public DomainEventsSerializer(ExtendedActorSystem system) : base(system) /// A byte array containing the serialized object public override byte[] ToBinary(object obj) { - // Put the code that serializes the object here - // ... ... - return null; + using (var stream = new MemoryStream()) + using (var writer = new StreamWriter(stream)) + { + _serializer.Serialize(writer,obj); + return stream.ToArray(); + } } /// @@ -44,9 +70,9 @@ public override byte[] ToBinary(object obj) /// The object contained in the array public override object FromBinary(byte[] bytes, Type type) { - // Put your code that deserializes here - // ... ... - return null; + using (var stream = new MemoryStream(bytes)) + using (var reader = new StreamReader(stream)) + return _serializer.Deserialize(reader, type);; } } } \ No newline at end of file diff --git a/GridDomain.Node/GridDomain.Node.csproj b/GridDomain.Node/GridDomain.Node.csproj index 542a629a..53a4a4af 100644 --- a/GridDomain.Node/GridDomain.Node.csproj +++ b/GridDomain.Node/GridDomain.Node.csproj @@ -297,7 +297,7 @@ - + diff --git a/GridDomain.Node/GridDomainNode.cs b/GridDomain.Node/GridDomainNode.cs index 83d5420f..5d9c3028 100644 --- a/GridDomain.Node/GridDomainNode.cs +++ b/GridDomain.Node/GridDomainNode.cs @@ -10,10 +10,12 @@ using Akka.Monitoring; using Akka.Monitoring.ApplicationInsights; using Akka.Monitoring.PerformanceCounters; +using Akka.Serialization; using GridDomain.Common; using GridDomain.CQRS; using GridDomain.CQRS.Messaging; using GridDomain.CQRS.Messaging.Akka; +using GridDomain.EventSourcing; using GridDomain.EventSourcing.Adapters; using GridDomain.EventSourcing.VersionedTypeSerialization; using GridDomain.Logging; @@ -132,8 +134,15 @@ private void OnSystemTermination(Task obj) public void Start(IDbConfiguration databaseConfiguration) { Systems = _actorSystemFactory.Invoke(); + + _transportMode = Systems.Length > 1 ? TransportMode.Cluster : TransportMode.Standalone; System = Systems.First(); + + DomainEventsSerializer = + System.Serialization.FindSerializerForType(typeof(DomainEvent)) as DomainEventsJsonSerializer; + + System.WhenTerminated.ContinueWith(OnSystemTermination); System.RegisterOnTermination(OnSystemTermination); System.AddDependencyResolver(new UnityDependencyResolver(Container, System)); @@ -183,6 +192,8 @@ private void ConfigureContainer(IUnityContainer unityContainer, public EventsAdaptersCatalog EventsAdaptersCatalog { get; } = AkkaDomainEventsAdapter.UpgradeChain; + public DomainEventsJsonSerializer DomainEventsSerializer { get; private set; } + public void Stop() { if (_stopping) return; From f16cf1052262fd6432828abf224b713829f01eb5 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Sun, 2 Oct 2016 02:29:52 +0300 Subject: [PATCH 08/33] saving events in unicode format to allow sql server produce human-readable view of payload red tests for json converter in gridnode --- .../Object_upgrade_by_domain_events.cs | 118 ++++++++++++++++++ .../GridDomain.Tests.csproj | 1 + GridDomain.EventSourcing/DomainEvent.cs | 2 +- GridDomain.Node/1.4-1.5_migration.sql | 14 +++ .../Configuration/Akka/Hocon/ActorConfig.cs | 17 ++- .../Configuration/Akka/Hocon/LogConfig.cs | 13 +- GridDomain.Node/DomainEventsJsonSerializer.cs | 32 +++-- GridDomain.Node/GridDomainNode.cs | 5 +- .../AutoTestAkkaDbConfiguration.cs | 4 +- 9 files changed, 179 insertions(+), 27 deletions(-) create mode 100644 GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs b/GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs new file mode 100644 index 00000000..29c04fb4 --- /dev/null +++ b/GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs @@ -0,0 +1,118 @@ +using System; +using System.Linq; +using Akka.Actor; +using Akka.Persistence; +using GridDomain.EventSourcing; +using GridDomain.EventSourcing.Adapters; +using GridDomain.Node.Configuration.Akka.Hocon; +using GridDomain.Tests.CommandsExecution; +using GridDomain.Tests.SampleDomain; +using NUnit.Framework; + +namespace GridDomain.Tests.EventsUpgrade +{ + [TestFixture] + class GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal : PersistentSampleDomainTests + { + + class EventA : DomainEvent + { + public IOrder Order { get; } + + public EventA(Guid sourceId, IOrder order) : base(sourceId) + { + Order = order; + } + } + + class EventB : DomainEvent + { + public IOrder Order { get; } + + public EventB(Guid sourceId, IOrder order) : base(sourceId) + { + Order = order; + } + } + + internal interface IOrder + { + string Number { get; } + } + + class BookOrder_V1 : IOrder + { + public BookOrder_V1(string number) + { + Number = number; + } + public string Number { get; } + } + + class BookOrder_V2 : IOrder + { + public BookOrder_V2(string number, int quantity) + { + Number = number; + Quantity = quantity; + } + + public string Number { get; } + public int Quantity { get; } + } + + class BookOrderAdapter : ObjectAdapter + { + public override BookOrder_V2 Convert(BookOrder_V1 value) + { + return new BookOrder_V2(value.Number,0); + } + } + + [Test] + public void GridNode_updates_objects_in_events_by_adapter() + { + GridNode.DomainEventsSerializer.Register(new BookOrderAdapter()); + var journal = Akka.Persistence.Persistence.Instance.Apply(GridNode.System).JournalFor(null); + + var orderA = new BookOrder_V1("A"); + var orderB = new BookOrder_V1("B"); + var id = Guid.NewGuid(); + var events = new DomainEvent[] + { + new EventA(id, orderA), + new EventB(id, orderB) + }; + + int seqNumber=0; + var envelop = + events.Select(e => new Akka.Persistence.AtomicWrite( + new Persistent(e, seqNumber++, "testId", e.GetType() + .AssemblyQualifiedShortName()))) + .Cast() + .ToArray(); + + var writeMsg = new WriteMessages(envelop, TestActor,1); + + journal.Tell(writeMsg); + + //var msg = ExpectMsg(); + + // Assert.IsInstanceOf(msg); + var loadMsg = new ReplayMessages(0,5,5,"testId",TestActor); + + journal.Tell(loadMsg); + + // var confirmWriteA = ExpectMsg(); + // var confirmWriteB = ExpectMsg(); + + var expectA = ExpectMsg(); + var expectB = ExpectMsg(); + + Assert.IsInstanceOf((expectA.Persistent.Payload as EventA)?.Order); + Assert.IsInstanceOf((expectB.Persistent.Payload as EventB)?.Order); + } + } + + +} \ No newline at end of file diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 7784db3c..65371d3b 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -291,6 +291,7 @@ + diff --git a/GridDomain.EventSourcing/DomainEvent.cs b/GridDomain.EventSourcing/DomainEvent.cs index cd755bb2..7a480228 100644 --- a/GridDomain.EventSourcing/DomainEvent.cs +++ b/GridDomain.EventSourcing/DomainEvent.cs @@ -7,7 +7,7 @@ namespace GridDomain.EventSourcing { - public class DomainEvent : ISourcedEvent//, ISerializable + public class DomainEvent : ISourcedEvent { public DomainEvent(Guid sourceId, DateTime? createdTime = null, Guid sagaId = default(Guid)) { diff --git a/GridDomain.Node/1.4-1.5_migration.sql b/GridDomain.Node/1.4-1.5_migration.sql index 2c4df73a..cc3f0c4d 100644 --- a/GridDomain.Node/1.4-1.5_migration.sql +++ b/GridDomain.Node/1.4-1.5_migration.sql @@ -1,5 +1,19 @@ -- helper function to convert between DATETIME2 and BIGINT as .NET ticks -- taken from: http://stackoverflow.com/questions/7386634/convert-sql-server-datetime-object-to-bigint-net-ticks + + +Create View HumanJournal AS +SELECT [PersistenceId] + ,[SequenceNr] + ,NetFxUtcTicksToDateTime ([Timestamp]) Time + ,[IsDeleted] + ,[Manifest] + ,CONVERT(NVARCHAR(max), [Payload], 0) Payload + ,[Tags] + FROM [AutoTestAkka].[dbo].[Journal] + + +-- GO CREATE FUNCTION [dbo].[Ticks] (@dt DATETIME) RETURNS BIGINT diff --git a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs index 9236709a..a2165f9b 100644 --- a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs +++ b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs @@ -1,4 +1,7 @@ using System; +using Akka.Serialization; +using CommonDomain; +using GridDomain.EventSourcing; namespace GridDomain.Node.Configuration.Akka.Hocon { @@ -25,19 +28,23 @@ protected ActorConfig(IAkkaNetworkAddress config) : this(config.PortNumber, conf public string Build() { string messageSerialization = ""; -//#if DEBUG -// messageSerialization = @"serialize-messages = on -// serialize-creators = on"; -//#endif +#if DEBUG + messageSerialization = @"serialize-messages = on + # serialize-creators = on"; +#endif var actorConfig = @" actor { "+messageSerialization+ @" serializers { - wire = ""Akka.Serialization.WireSerializer, Akka.Serialization.Wire"" + wire = """+typeof(WireSerializer).AssemblyQualifiedShortName()+ @""" + json = """+typeof(DomainEventsJsonSerializer).AssemblyQualifiedShortName() + @""" } serialization-bindings { + """ + typeof(DomainEvent).AssemblyQualifiedShortName() + @""" = json + """ + typeof(IAggregate).AssemblyQualifiedShortName() + @""" = json ""System.Object"" = wire + } }"; diff --git a/GridDomain.Node/Configuration/Akka/Hocon/LogConfig.cs b/GridDomain.Node/Configuration/Akka/Hocon/LogConfig.cs index e0da06ef..d3031129 100644 --- a/GridDomain.Node/Configuration/Akka/Hocon/LogConfig.cs +++ b/GridDomain.Node/Configuration/Akka/Hocon/LogConfig.cs @@ -22,11 +22,14 @@ public string Build() logConfig += @" loggers=["""+typeof(SerilogExtendedLogger).AssemblyQualifiedShortName() + @"""] - actor.debug { - #receive = on - #autoreceive = on - #lifecycle = on - #event-stream = on + actor.debug {"+ +#if DEBUG + @"receive = on + autoreceive = on + lifecycle = on + event-stream = on " +#endif + +@" unhandled = on }"; diff --git a/GridDomain.Node/DomainEventsJsonSerializer.cs b/GridDomain.Node/DomainEventsJsonSerializer.cs index 3897836b..e1ff87dc 100644 --- a/GridDomain.Node/DomainEventsJsonSerializer.cs +++ b/GridDomain.Node/DomainEventsJsonSerializer.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using Akka.Actor; using Akka.Serialization; +using Akka.Util; using GridDomain.EventSourcing.Adapters; using Newtonsoft.Json; @@ -13,8 +15,9 @@ public class DomainEventsJsonSerializer : Serializer { private JsonSerializer _serializer; private readonly List _converters = new List(); + private JsonSerializerSettings _jsonSerializerSettings; + - public DomainEventsJsonSerializer(ExtendedActorSystem system) : base(system) { Init(); @@ -24,13 +27,16 @@ public void Register(JsonConverter converter) { _converters.Add(converter); } + public void Register(ObjectAdapter converter) + { + Register((JsonConverter)converter); + } public void Init() { - var jsonSerializerSettings = DomainEventSerialization.GetDefault(); - foreach (var converter in _converters) - jsonSerializerSettings.Converters.Add(converter); - _serializer = JsonSerializer.Create(jsonSerializerSettings); + _jsonSerializerSettings = DomainEventSerialization.GetDefault(); + _jsonSerializerSettings.Converters = _converters; + _serializer = JsonSerializer.Create(_jsonSerializerSettings); } /// @@ -53,12 +59,16 @@ public void Init() /// A byte array containing the serialized object public override byte[] ToBinary(object obj) { - using (var stream = new MemoryStream()) - using (var writer = new StreamWriter(stream)) - { - _serializer.Serialize(writer,obj); - return stream.ToArray(); - } + //using (var stream = new MemoryStream()) + //using (var streamw = new StreamWriter(stream)) + //using (var writer = new JsonTextWriter(streamw)) + //{ + // _serializer.Serialize(writer,obj); + // return stream.ToArray(); + //} + //TODO: use faster realization with reusable serializer + var stringJson = JsonConvert.SerializeObject(obj, _jsonSerializerSettings); + return Encoding.Unicode.GetBytes(stringJson); } /// diff --git a/GridDomain.Node/GridDomainNode.cs b/GridDomain.Node/GridDomainNode.cs index 5d9c3028..46b5647f 100644 --- a/GridDomain.Node/GridDomainNode.cs +++ b/GridDomain.Node/GridDomainNode.cs @@ -139,9 +139,8 @@ public void Start(IDbConfiguration databaseConfiguration) _transportMode = Systems.Length > 1 ? TransportMode.Cluster : TransportMode.Standalone; System = Systems.First(); - DomainEventsSerializer = - System.Serialization.FindSerializerForType(typeof(DomainEvent)) as DomainEventsJsonSerializer; - + DomainEventsSerializer = (DomainEventsJsonSerializer) + System.Serialization.FindSerializerForType(typeof(DomainEvent)); System.WhenTerminated.ContinueWith(OnSystemTermination); System.RegisterOnTermination(OnSystemTermination); diff --git a/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs b/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs index 5d88d9b4..3eba8e86 100644 --- a/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs +++ b/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs @@ -7,10 +7,10 @@ namespace GridDomain.Tests.Framework.Configuration public class AutoTestAkkaDbConfiguration : IAkkaDbConfiguration { public string SnapshotConnectionString - => ConfigurationManager.ConnectionStrings["GridDomainWriteTestString"]?.ConnectionString ?? "Server=tcp:soloinfra.cloudapp.net,5099;Database=sandboxMembershipWriteAutoTests;User ID=solomoto;Password=s0l0moto;MultipleActiveResultSets=True"; + => ConfigurationManager.ConnectionStrings["GridDomainWriteTestString"]?.ConnectionString ?? "Server=(local);Database=AutoTestAkka;Integrated Security = true;"; public string JournalConnectionString - => ConfigurationManager.ConnectionStrings["GridDomainWriteTestString"]?.ConnectionString ?? "Server=tcp:soloinfra.cloudapp.net,5099;Database=sandboxMembershipWriteAutoTests;User ID=solomoto;Password=s0l0moto;MultipleActiveResultSets=True"; + => ConfigurationManager.ConnectionStrings["GridDomainWriteTestString"]?.ConnectionString ?? "Server=(local);Database=AutoTestAkka;Integrated Security = true;"; public string MetadataTableName => "Metadata"; public string JournalTableName => "Journal"; From c826d8e2a0ffcf215053dde08190cfc6af43a3b9 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Mon, 3 Oct 2016 18:38:59 +0300 Subject: [PATCH 09/33] nunit packages version upgrade, moved test to acceptance project --- GridDomain.Domain.Tests/GridDomain.Tests.csproj | 14 ++------------ GridDomain.Domain.Tests/packages.config | 2 +- ...ith_upgraded_event_with_new_field_Persistent.cs | 0 ..._in_domain_events_after_save_load_by_journal.cs | 10 +++++----- .../GridDomain.Tests.Acceptance.csproj | 14 +++++++++----- GridDomain.Tests.Acceptance/packages.config | 2 +- .../GridDomain.Tests.Framework.csproj | 4 ++-- GridDomain.Tests.Framework/packages.config | 7 ++++++- 8 files changed, 26 insertions(+), 27 deletions(-) rename GridDomain.Tests.Acceptance/EventsUpgrade/{SampleDomain => }/Given_aggregate_with_upgraded_event_with_new_field_Persistent.cs (100%) rename GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs => GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs (90%) diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 65371d3b..6b915daf 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -58,14 +58,6 @@ ..\packages\Akka.Persistence.1.1.2.30-beta\lib\net45\Akka.Persistence.dll True - - ..\packages\Akka.Persistence.Sql.Common.1.1.1.28-beta\lib\net45\Akka.Persistence.Sql.Common.dll - True - - - ..\packages\Akka.Persistence.SqlServer.1.1.1.5-beta\lib\net45\Akka.Persistence.SqlServer.dll - True - ..\packages\Akka.Persistence.TestKit.1.1.2.30-beta\lib\net45\Akka.Persistence.TestKit.dll True @@ -82,9 +74,8 @@ ..\packages\Akka.TestKit.1.1.2\lib\net45\Akka.TestKit.dll True - - ..\packages\Akka.TestKit.NUnit3.1.1.1\lib\net45\Akka.TestKit.NUnit3.dll - True + + ..\packages\Akka.TestKit.NUnit3.1.1.2\lib\net45\Akka.TestKit.NUnit3.dll ..\packages\Automatonymous.3.3.0\lib\net452\Automatonymous.dll @@ -291,7 +282,6 @@ - diff --git a/GridDomain.Domain.Tests/packages.config b/GridDomain.Domain.Tests/packages.config index 4cf3e43a..f018186c 100644 --- a/GridDomain.Domain.Tests/packages.config +++ b/GridDomain.Domain.Tests/packages.config @@ -11,7 +11,7 @@ - + diff --git a/GridDomain.Tests.Acceptance/EventsUpgrade/SampleDomain/Given_aggregate_with_upgraded_event_with_new_field_Persistent.cs b/GridDomain.Tests.Acceptance/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field_Persistent.cs similarity index 100% rename from GridDomain.Tests.Acceptance/EventsUpgrade/SampleDomain/Given_aggregate_with_upgraded_event_with_new_field_Persistent.cs rename to GridDomain.Tests.Acceptance/EventsUpgrade/Given_aggregate_with_upgraded_event_with_new_field_Persistent.cs diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs similarity index 90% rename from GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs rename to GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs index 29c04fb4..b09cd41e 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Object_upgrade_by_domain_events.cs +++ b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs @@ -73,7 +73,7 @@ public override BookOrder_V2 Convert(BookOrder_V1 value) public void GridNode_updates_objects_in_events_by_adapter() { GridNode.DomainEventsSerializer.Register(new BookOrderAdapter()); - var journal = Akka.Persistence.Persistence.Instance.Apply(GridNode.System).JournalFor(null); + var journal = Persistence.Instance.Apply(GridNode.System).JournalFor(null); var orderA = new BookOrder_V1("A"); var orderB = new BookOrder_V1("B"); @@ -94,17 +94,17 @@ public void GridNode_updates_objects_in_events_by_adapter() var writeMsg = new WriteMessages(envelop, TestActor,1); - journal.Tell(writeMsg); + journal.Ask(writeMsg).Wait(); //var msg = ExpectMsg(); - // Assert.IsInstanceOf(msg); + // Assert.IsInstanceOf(msg); var loadMsg = new ReplayMessages(0,5,5,"testId",TestActor); journal.Tell(loadMsg); - // var confirmWriteA = ExpectMsg(); - // var confirmWriteB = ExpectMsg(); + // var confirmWriteA = ExpectMsg(); + // var confirmWriteB = ExpectMsg(); var expectA = ExpectMsg(); var expectB = ExpectMsg(); diff --git a/GridDomain.Tests.Acceptance/GridDomain.Tests.Acceptance.csproj b/GridDomain.Tests.Acceptance/GridDomain.Tests.Acceptance.csproj index c6dcb3d0..aa02e0cb 100644 --- a/GridDomain.Tests.Acceptance/GridDomain.Tests.Acceptance.csproj +++ b/GridDomain.Tests.Acceptance/GridDomain.Tests.Acceptance.csproj @@ -86,8 +86,8 @@ ..\packages\Akka.TestKit.1.1.2\lib\net45\Akka.TestKit.dll True - - ..\packages\Akka.TestKit.NUnit3.1.1.1\lib\net45\Akka.TestKit.NUnit3.dll + + ..\packages\Akka.TestKit.NUnit3.1.1.2\lib\net45\Akka.TestKit.NUnit3.dll True @@ -166,8 +166,8 @@ ..\packages\NMoneys.4.5.0\lib\Net40-client\NMoneys.dll True - - ..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll + + ..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll True @@ -230,7 +230,8 @@ - + + @@ -357,6 +358,9 @@ + + + -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 5751812b6b27a4eb1c3b2182c58a57436185126d Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Mon, 3 Oct 2016 18:50:50 +0300 Subject: [PATCH 12/33] reference to nunit upgrade --- GridDomain.Domain.Tests/GridDomain.Tests.csproj | 4 ++-- GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 6b915daf..8258f765 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -161,8 +161,8 @@ ..\packages\NMoneys.4.5.0\lib\Net40-client\NMoneys.dll True - - ..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll + + ..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll True diff --git a/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj b/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj index 35c4bff8..b2d67760 100644 --- a/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj +++ b/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj @@ -93,8 +93,8 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll + + ..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll True From cb79319d0ac6754bd26ca36fb5dc6807f006d6a3 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Mon, 3 Oct 2016 18:51:14 +0300 Subject: [PATCH 13/33] Revert "reference to nunit upgrade" This reverts commit fe4a12ccbba744d1e7c3e4cb7a97cd9ce8c56776. --- GridDomain.Tests.Acceptance/App.config | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 GridDomain.Tests.Acceptance/App.config diff --git a/GridDomain.Tests.Acceptance/App.config b/GridDomain.Tests.Acceptance/App.config new file mode 100644 index 00000000..f243392d --- /dev/null +++ b/GridDomain.Tests.Acceptance/App.config @@ -0,0 +1,105 @@ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 18abecfdf5334e12e8f99d44efc7f8be90990cff Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Mon, 3 Oct 2016 20:44:48 +0300 Subject: [PATCH 14/33] removed schema declaration witch lead to null reference exception from nuget --- GridDomain.Scheduling/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GridDomain.Scheduling/packages.config b/GridDomain.Scheduling/packages.config index cd32ff6c..cdee14c3 100644 --- a/GridDomain.Scheduling/packages.config +++ b/GridDomain.Scheduling/packages.config @@ -1,5 +1,5 @@  - + From 868e9cef6b3539e242e7c9a161f6e7f2189d4e9f Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 4 Oct 2016 17:46:32 +0300 Subject: [PATCH 15/33] Fixed packages issues --- .../EventsUpgrade/VersionedTypeNameTests.cs | 1 - GridDomain.Domain.Tests/packages.config | 2 +- GridDomain.Scheduling/WebUI/WebUIWrapper.cs | 1 + GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj | 4 ++-- GridDomain.Tests.Framework/packages.config | 2 +- GridGomain.Tests.Stress/packages.config | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeNameTests.cs b/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeNameTests.cs index 0d910e0b..2d443f80 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeNameTests.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/VersionedTypeNameTests.cs @@ -2,7 +2,6 @@ using GridDomain.EventSourcing.VersionedTypeSerialization; using GridDomain.Tests.EventsUpgrade.Events; using NUnit.Framework; -using QuickGraph.Serialization; namespace GridDomain.Tests.EventsUpgrade { diff --git a/GridDomain.Domain.Tests/packages.config b/GridDomain.Domain.Tests/packages.config index f018186c..370f1b11 100644 --- a/GridDomain.Domain.Tests/packages.config +++ b/GridDomain.Domain.Tests/packages.config @@ -1,5 +1,5 @@  - + diff --git a/GridDomain.Scheduling/WebUI/WebUIWrapper.cs b/GridDomain.Scheduling/WebUI/WebUIWrapper.cs index 9d6b6f56..0facef09 100644 --- a/GridDomain.Scheduling/WebUI/WebUIWrapper.cs +++ b/GridDomain.Scheduling/WebUI/WebUIWrapper.cs @@ -2,6 +2,7 @@ using Microsoft.Owin.Hosting; using Quartz; + namespace GridDomain.Scheduling.WebUI { public class WebUiWrapper : IWebUiWrapper diff --git a/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj b/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj index c8e473d0..07987cfc 100644 --- a/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj +++ b/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj @@ -107,8 +107,8 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll + + ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll True diff --git a/GridDomain.Tests.Framework/packages.config b/GridDomain.Tests.Framework/packages.config index 152fea13..cbeebfc8 100644 --- a/GridDomain.Tests.Framework/packages.config +++ b/GridDomain.Tests.Framework/packages.config @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/GridGomain.Tests.Stress/packages.config b/GridGomain.Tests.Stress/packages.config index 5ae612e4..8564c360 100644 --- a/GridGomain.Tests.Stress/packages.config +++ b/GridGomain.Tests.Stress/packages.config @@ -1,5 +1,5 @@  - + From f82de4e609bb58426afabe4fb2d6e50f958977ab Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 4 Oct 2016 18:08:59 +0300 Subject: [PATCH 16/33] sql schema updated to latest akka.persistence.sql --- .../{1.4-1.5_migration.sql => Database schema.sql} | 6 ++++-- ...objects_in_domain_events_after_save_load_by_journal.cs | 8 ++++---- .../GridDomain.Tests.Framework.csproj | 4 ++-- GridDomain.Tests.Framework/packages.config | 2 +- GridGomain.Tests.Stress/packages.config | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) rename GridDomain.Node/{1.4-1.5_migration.sql => Database schema.sql} (93%) diff --git a/GridDomain.Node/1.4-1.5_migration.sql b/GridDomain.Node/Database schema.sql similarity index 93% rename from GridDomain.Node/1.4-1.5_migration.sql rename to GridDomain.Node/Database schema.sql index cc3f0c4d..87b5917f 100644 --- a/GridDomain.Node/1.4-1.5_migration.sql +++ b/GridDomain.Node/Database schema.sql @@ -1,8 +1,11 @@ -- helper function to convert between DATETIME2 and BIGINT as .NET ticks -- taken from: http://stackoverflow.com/questions/7386634/convert-sql-server-datetime-object-to-bigint-net-ticks +ALTER TABLE Journal DROP CONSTRAINT PK_Journal; +ALTER TABLE Journal ADD Ordering BIGINT IDENTITY(1,1) PRIMARY KEY NOT NULL; +ALTER TABLE Journal ADD CONSTRAINT QU_Journal UNIQUE (PersistenceID, SequenceNr); -Create View HumanJournal AS +create View HumanJournal AS SELECT [PersistenceId] ,[SequenceNr] ,NetFxUtcTicksToDateTime ([Timestamp]) Time @@ -12,7 +15,6 @@ SELECT [PersistenceId] ,[Tags] FROM [AutoTestAkka].[dbo].[Journal] - -- GO CREATE FUNCTION [dbo].[Ticks] (@dt DATETIME) diff --git a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs index b09cd41e..8ba0485b 100644 --- a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs +++ b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs @@ -96,15 +96,15 @@ public void GridNode_updates_objects_in_events_by_adapter() journal.Ask(writeMsg).Wait(); - //var msg = ExpectMsg(); + var msg = ExpectMsg(); - // Assert.IsInstanceOf(msg); + Assert.IsInstanceOf(msg); var loadMsg = new ReplayMessages(0,5,5,"testId",TestActor); journal.Tell(loadMsg); - // var confirmWriteA = ExpectMsg(); - // var confirmWriteB = ExpectMsg(); + var confirmWriteA = ExpectMsg(); + var confirmWriteB = ExpectMsg(); var expectA = ExpectMsg(); var expectB = ExpectMsg(); diff --git a/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj b/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj index 07987cfc..773437c9 100644 --- a/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj +++ b/GridDomain.Tests.Framework/GridDomain.Tests.Framework.csproj @@ -107,8 +107,8 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True - - ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll + + ..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll True diff --git a/GridDomain.Tests.Framework/packages.config b/GridDomain.Tests.Framework/packages.config index cbeebfc8..152fea13 100644 --- a/GridDomain.Tests.Framework/packages.config +++ b/GridDomain.Tests.Framework/packages.config @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/GridGomain.Tests.Stress/packages.config b/GridGomain.Tests.Stress/packages.config index 8564c360..6679313f 100644 --- a/GridGomain.Tests.Stress/packages.config +++ b/GridGomain.Tests.Stress/packages.config @@ -3,8 +3,8 @@ - - + + From 2056856e828268e36958ac2d37dd43d6ed19f06d Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 25 Oct 2016 18:42:19 +0300 Subject: [PATCH 17/33] green tests without message serialization --- GridDomain.Domain.Tests/GridDomain.Tests.csproj | 4 ++++ GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs | 4 ++-- GridDomain.Node/DomainEventsJsonSerializer.cs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/GridDomain.Domain.Tests/GridDomain.Tests.csproj b/GridDomain.Domain.Tests/GridDomain.Tests.csproj index 29350032..c14ac51a 100644 --- a/GridDomain.Domain.Tests/GridDomain.Tests.csproj +++ b/GridDomain.Domain.Tests/GridDomain.Tests.csproj @@ -58,9 +58,13 @@ ..\packages\Akka.Persistence.1.1.2.281-beta\lib\net45\Akka.Persistence.dll True + ..\packages\Akka.Persistence.Sql.Common.1.1.2.257-beta\lib\net45\Akka.Persistence.Sql.Common.dll + True + ..\packages\Akka.Persistence.SqlServer.1.1.1.158-beta\lib\net45\Akka.Persistence.SqlServer.dll + ..\packages\Akka.Persistence.TestKit.1.1.2.30-beta\lib\net45\Akka.Persistence.TestKit.dll True diff --git a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs index ea36fcb3..54aa0c6b 100644 --- a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs +++ b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs @@ -29,8 +29,8 @@ public string Build() { string messageSerialization = ""; #if DEBUG - messageSerialization = @"serialize-messages = on - # serialize-creators = on"; + messageSerialization = @"# serialize-messages = on + # serialize-creators = on"; #endif var actorConfig = @" actor { diff --git a/GridDomain.Node/DomainEventsJsonSerializer.cs b/GridDomain.Node/DomainEventsJsonSerializer.cs index e1ff87dc..8bcb18c0 100644 --- a/GridDomain.Node/DomainEventsJsonSerializer.cs +++ b/GridDomain.Node/DomainEventsJsonSerializer.cs @@ -81,7 +81,7 @@ public override byte[] ToBinary(object obj) public override object FromBinary(byte[] bytes, Type type) { using (var stream = new MemoryStream(bytes)) - using (var reader = new StreamReader(stream)) + using (var reader = new StreamReader(stream,Encoding.Unicode)) return _serializer.Deserialize(reader, type);; } } From 064bf334a0486c6f7c62755caaafa68e3660e85f Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 25 Oct 2016 19:26:58 +0300 Subject: [PATCH 18/33] updating references --- .../EventsUpgrade/Upgrade_object_in_property.cs | 15 --------------- ...in_domain_events_after_save_load_by_journal.cs | 11 ++++++++--- .../Configuration/AutoTestAkkaDbConfiguration.cs | 6 ++++-- .../ExtendedNodeCommandTest.cs | 2 +- .../GridGomain.Tests.Stress.csproj | 4 ++-- GridGomain.Tests.Stress/packages.config | 2 +- 6 files changed, 16 insertions(+), 24 deletions(-) diff --git a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs index 77bc0600..bf4a5e10 100644 --- a/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs +++ b/GridDomain.Domain.Tests/EventsUpgrade/Upgrade_object_in_property.cs @@ -80,20 +80,5 @@ public void Propert_is_upgraded() Assert.IsInstanceOf(restoredEvent.Payload); } - - //[Test] - //public void Root_object_also_Should_be_converted() - //{ - // var initialEvent = new SubObject_V1() {Name = "10", Value = "123"}; - // - // var settings = DomainEventSerialization.GetDefault(); - // settings.Converters.Add(new SubObjectConverter()); - // - // var serializedValue = JsonConvert.SerializeObject(initialEvent, settings); - // - // var restoredEvent = JsonConvert.DeserializeObject(serializedValue, settings); - // - // Assert.IsInstanceOf(restoredEvent); - //} } } diff --git a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs index 8ba0485b..7151eabd 100644 --- a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs +++ b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs @@ -12,7 +12,7 @@ namespace GridDomain.Tests.EventsUpgrade { [TestFixture] - class GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal : PersistentSampleDomainTests + class GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal : SampleDomainCommandExecutionTests { class EventA : DomainEvent @@ -69,12 +69,17 @@ public override BookOrder_V2 Convert(BookOrder_V1 value) } } + protected override bool InMemory { get; } = false; + [Test] public void GridNode_updates_objects_in_events_by_adapter() { GridNode.DomainEventsSerializer.Register(new BookOrderAdapter()); - var journal = Persistence.Instance.Apply(GridNode.System).JournalFor(null); + var persistenceExtension = Persistence.Instance.Apply(GridNode.System); + var settings = persistenceExtension.Settings; + var journal = persistenceExtension.JournalFor(null); + var orderA = new BookOrder_V1("A"); var orderB = new BookOrder_V1("B"); var id = Guid.NewGuid(); @@ -94,7 +99,7 @@ public void GridNode_updates_objects_in_events_by_adapter() var writeMsg = new WriteMessages(envelop, TestActor,1); - journal.Ask(writeMsg).Wait(); + journal.Ask(writeMsg);//.Wait(); var msg = ExpectMsg(); diff --git a/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs b/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs index 44fd3ef0..ac6d2022 100644 --- a/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs +++ b/GridDomain.Tests.Framework/Configuration/AutoTestAkkaDbConfiguration.cs @@ -9,10 +9,12 @@ public class AutoTestAkkaDbConfiguration : IAkkaDbConfiguration private const string JournalConnectionStringName = "WriteModel"; public string SnapshotConnectionString - => ConfigurationManager.ConnectionStrings[JournalConnectionStringName]?.ConnectionString ?? "Server=tcp:soloinfra.cloudapp.net,5099;Database=sandboxMembershipWriteAutoTests;User ID=solomoto;Password=s0l0moto;MultipleActiveResultSets=True"; + => ConfigurationManager.ConnectionStrings[JournalConnectionStringName]?.ConnectionString ?? + "Server=(local); Database = AutoTestAkka; Integrated Security = true; MultipleActiveResultSets = True"; public string JournalConnectionString - => ConfigurationManager.ConnectionStrings[JournalConnectionStringName]?.ConnectionString ?? "Server=tcp:soloinfra.cloudapp.net,5099;Database=sandboxMembershipWriteAutoTests;User ID=solomoto;Password=s0l0moto;MultipleActiveResultSets=True"; + => ConfigurationManager.ConnectionStrings[JournalConnectionStringName]?.ConnectionString ?? + "Server=(local); Database = AutoTestAkka; Integrated Security = true; MultipleActiveResultSets = True"; public string MetadataTableName => "Metadata"; public string JournalTableName => "Journal"; diff --git a/GridDomain.Tests.Framework/ExtendedNodeCommandTest.cs b/GridDomain.Tests.Framework/ExtendedNodeCommandTest.cs index ee32f678..35317f3f 100644 --- a/GridDomain.Tests.Framework/ExtendedNodeCommandTest.cs +++ b/GridDomain.Tests.Framework/ExtendedNodeCommandTest.cs @@ -18,7 +18,7 @@ namespace GridDomain.Tests.Framework { public abstract class ExtendedNodeCommandTest : NodeCommandsTest { - protected readonly bool InMemory; + protected virtual bool InMemory { get; } protected static readonly AutoTestAkkaConfiguration AkkaCfg = new AutoTestAkkaConfiguration(); protected abstract IContainerConfiguration CreateConfiguration(); protected abstract IMessageRouteMap CreateMap(); diff --git a/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj b/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj index c05001ab..0b37f289 100644 --- a/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj +++ b/GridGomain.Tests.Stress/GridGomain.Tests.Stress.csproj @@ -45,8 +45,8 @@ ..\packages\Akka.DI.Unity.1.1.0\lib\net45\Akka.DI.Unity.dll True - - ..\packages\Akka.Persistence.1.1.2.257-beta\lib\net45\Akka.Persistence.dll + + ..\packages\Akka.Persistence.1.1.2.281-beta\lib\net45\Akka.Persistence.dll True diff --git a/GridGomain.Tests.Stress/packages.config b/GridGomain.Tests.Stress/packages.config index 0ba97428..cac71dc1 100644 --- a/GridGomain.Tests.Stress/packages.config +++ b/GridGomain.Tests.Stress/packages.config @@ -1,7 +1,7 @@  - + From 1c5453a5e4ce69c13e33f136abe3ec67f0b3e781 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 25 Oct 2016 20:18:34 +0300 Subject: [PATCH 19/33] extracted methods for save\load messages directly from journal --- ...omain_events_after_save_load_by_journal.cs | 41 ++++------------ .../NodeCommandsTest.cs | 49 +++++++++++++++++-- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs index 7151eabd..2d466800 100644 --- a/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs +++ b/GridDomain.Tests.Acceptance/EventsUpgrade/GridNode_should_upgrade_objects_in_domain_events_after_save_load_by_journal.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Akka.Actor; using Akka.Persistence; @@ -70,15 +71,14 @@ public override BookOrder_V2 Convert(BookOrder_V1 value) } protected override bool InMemory { get; } = false; + protected override bool ClearDataOnStart { get; } = true; + + [Test] public void GridNode_updates_objects_in_events_by_adapter() { GridNode.DomainEventsSerializer.Register(new BookOrderAdapter()); - var persistenceExtension = Persistence.Instance.Apply(GridNode.System); - - var settings = persistenceExtension.Settings; - var journal = persistenceExtension.JournalFor(null); var orderA = new BookOrder_V1("A"); var orderB = new BookOrder_V1("B"); @@ -88,34 +88,13 @@ public void GridNode_updates_objects_in_events_by_adapter() new EventA(id, orderA), new EventB(id, orderB) }; + SaveToJournal(events); + var loadedEvents = LoadFromJournal("testId", 2).ToArray(); + var expectA = loadedEvents.OfType().FirstOrDefault(); + var expectB = loadedEvents.OfType().FirstOrDefault(); - int seqNumber=0; - var envelop = - events.Select(e => new Akka.Persistence.AtomicWrite( - new Persistent(e, seqNumber++, "testId", e.GetType() - .AssemblyQualifiedShortName()))) - .Cast() - .ToArray(); - - var writeMsg = new WriteMessages(envelop, TestActor,1); - - journal.Ask(writeMsg);//.Wait(); - - var msg = ExpectMsg(); - - Assert.IsInstanceOf(msg); - var loadMsg = new ReplayMessages(0,5,5,"testId",TestActor); - - journal.Tell(loadMsg); - - var confirmWriteA = ExpectMsg(); - var confirmWriteB = ExpectMsg(); - - var expectA = ExpectMsg(); - var expectB = ExpectMsg(); - - Assert.IsInstanceOf((expectA.Persistent.Payload as EventA)?.Order); - Assert.IsInstanceOf((expectB.Persistent.Payload as EventB)?.Order); + Assert.IsInstanceOf(expectA.Order); + Assert.IsInstanceOf(expectB.Order); } } diff --git a/GridDomain.Tests.Framework/NodeCommandsTest.cs b/GridDomain.Tests.Framework/NodeCommandsTest.cs index 6ebc2917..d97609dc 100644 --- a/GridDomain.Tests.Framework/NodeCommandsTest.cs +++ b/GridDomain.Tests.Framework/NodeCommandsTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -16,6 +17,7 @@ using GridDomain.Node.AkkaMessaging; using GridDomain.Node.AkkaMessaging.Waiting; using GridDomain.Node.Configuration.Akka; +using GridDomain.Node.Configuration.Akka.Hocon; using GridDomain.Node.Configuration.Persistence; using GridDomain.Tests.Framework.Configuration; using Microsoft.Practices.Unity; @@ -29,11 +31,11 @@ public abstract class NodeCommandsTest : TestKit protected GridDomainNode GridNode; private readonly Stopwatch _watch = new Stopwatch(); - private readonly bool _clearDataOnStart; + protected virtual bool ClearDataOnStart { get; } = false; protected NodeCommandsTest(string config, string name = null, bool clearDataOnStart = true) : base(config, name) { - _clearDataOnStart = clearDataOnStart; + ClearDataOnStart = clearDataOnStart; } protected abstract TimeSpan Timeout { get; } @@ -58,7 +60,7 @@ public void Init() LogManager.SetLoggerFactory(new AutoTestLogFactory()); var autoTestGridDomainConfiguration = new AutoTestLocalDbConfiguration(); - if (_clearDataOnStart) + if (ClearDataOnStart) TestDbTools.ClearData(autoTestGridDomainConfiguration, AkkaConf.Persistence); GridNode = CreateGridDomainNode(AkkaConf, autoTestGridDomainConfiguration); @@ -190,5 +192,46 @@ private void Execute(ICommand[] commands) ((ICommandExecutor)GridNode).Execute(commands); } + + + protected void SaveToJournal(params object[] messages) + { + var persistenceExtension = Persistence.Instance.Get(GridNode.System) ?? Persistence.Instance.Apply(GridNode.System); + + var settings = persistenceExtension.Settings; + var journal = persistenceExtension.JournalFor(null); + + int seqNumber = 0; + var envelop = + messages.Select(e => new Akka.Persistence.AtomicWrite( + new Persistent(e, seqNumber++, "testId", e.GetType() + .AssemblyQualifiedShortName()))) + .Cast() + .ToArray(); + + var writeMsg = new WriteMessages(envelop, TestActor, 1); + + journal.Ask(writeMsg); + + var msg = ExpectMsg(); + + Assert.IsInstanceOf(msg); + } + + + protected IEnumerable LoadFromJournal(string persistenceId, int expectedCount) + { + var persistenceExtension = Persistence.Instance.Get(GridNode.System) ?? Persistence.Instance.Apply(GridNode.System); + var settings = persistenceExtension.Settings; + var journal = persistenceExtension.JournalFor(null); + + var loadMsg = new ReplayMessages(0, long.MaxValue, long.MaxValue, persistenceId, TestActor); + + journal.Tell(loadMsg); + + for (int i = 0; i < expectedCount; i++) + yield return FishForMessage(m => m.Persistent.PersistenceId == persistenceId).Persistent.Payload; + } + } } \ No newline at end of file From a800468d9dff47ecfcff4e0fe04543f5719d53c9 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 25 Oct 2016 20:42:58 +0300 Subject: [PATCH 20/33] Green test for domain event serializers adapters --- GridDomain.Node/DomainEventsJsonSerializer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/GridDomain.Node/DomainEventsJsonSerializer.cs b/GridDomain.Node/DomainEventsJsonSerializer.cs index 8bcb18c0..694423c9 100644 --- a/GridDomain.Node/DomainEventsJsonSerializer.cs +++ b/GridDomain.Node/DomainEventsJsonSerializer.cs @@ -26,6 +26,7 @@ public DomainEventsJsonSerializer(ExtendedActorSystem system) : base(system) public void Register(JsonConverter converter) { _converters.Add(converter); + Init(); } public void Register(ObjectAdapter converter) { From c33da1269216414711eb27dd7672abe9d3fdcbe7 Mon Sep 17 00:00:00 2001 From: "andrey.leskov" Date: Tue, 25 Oct 2016 20:49:01 +0300 Subject: [PATCH 21/33] disabled json serializer for a while --- GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs | 4 ++-- .../{Database schema.sql => Database schema full.sql} | 2 +- GridDomain.Node/DomainEventsJsonSerializer.cs | 1 - GridDomain.Node/GridDomain.Node.csproj | 1 + ...ade_objects_in_domain_events_after_save_load_by_journal.cs | 2 -- 5 files changed, 4 insertions(+), 6 deletions(-) rename GridDomain.Node/{Database schema.sql => Database schema full.sql} (99%) diff --git a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs index 54aa0c6b..ef823f3c 100644 --- a/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs +++ b/GridDomain.Node/Configuration/Akka/Hocon/ActorConfig.cs @@ -41,8 +41,8 @@ public string Build() } serialization-bindings { - """ + typeof(DomainEvent).AssemblyQualifiedShortName() + @""" = json - """ + typeof(IAggregate).AssemblyQualifiedShortName() + @""" = json + #""" + typeof(DomainEvent).AssemblyQualifiedShortName() + @""" = json + #""" + typeof(IAggregate).AssemblyQualifiedShortName() + @""" = json ""System.Object"" = wire } diff --git a/GridDomain.Node/Database schema.sql b/GridDomain.Node/Database schema full.sql similarity index 99% rename from GridDomain.Node/Database schema.sql rename to GridDomain.Node/Database schema full.sql index 87b5917f..e5e75959 100644 --- a/GridDomain.Node/Database schema.sql +++ b/GridDomain.Node/Database schema full.sql @@ -4,7 +4,7 @@ ALTER TABLE Journal DROP CONSTRAINT PK_Journal; ALTER TABLE Journal ADD Ordering BIGINT IDENTITY(1,1) PRIMARY KEY NOT NULL; ALTER TABLE Journal ADD CONSTRAINT QU_Journal UNIQUE (PersistenceID, SequenceNr); - +GO create View HumanJournal AS SELECT [PersistenceId] ,[SequenceNr] diff --git a/GridDomain.Node/DomainEventsJsonSerializer.cs b/GridDomain.Node/DomainEventsJsonSerializer.cs index 694423c9..eb6015f6 100644 --- a/GridDomain.Node/DomainEventsJsonSerializer.cs +++ b/GridDomain.Node/DomainEventsJsonSerializer.cs @@ -10,7 +10,6 @@ namespace GridDomain.Node { - public class DomainEventsJsonSerializer : Serializer { private JsonSerializer _serializer; diff --git a/GridDomain.Node/GridDomain.Node.csproj b/GridDomain.Node/GridDomain.Node.csproj index 2aeb1a59..0c7ca9d0 100644 --- a/GridDomain.Node/GridDomain.Node.csproj +++ b/GridDomain.Node/GridDomain.Node.csproj @@ -382,6 +382,7 @@ +