Skip to content

Commit 1aaf616

Browse files
author
andrey.leskov
committed
Aggregate uses itself as IMemento to enable default snapshotting.
1 parent a9bf68f commit 1aaf616

6 files changed

Lines changed: 119 additions & 2 deletions

File tree

GridDomain.Domain.Tests/GridDomain.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
</Reference>
237237
</ItemGroup>
238238
<ItemGroup>
239+
<Compile Include="Serialization\Aggregate_can_be_used_for_snapshotting_by_defaut.cs" />
239240
<Compile Include="Serialization\Domain_event_should_retain_createdTime_defaulted_in_constructor.cs" />
240241
<Compile Include="Serialization\Domain_event_should_retain_sagaId_and_createdTime.cs" />
241242
<Compile Include="Types_should_be_deserializable.cs" />
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using CommonDomain;
3+
using GridDomain.EventSourcing;
4+
using GridDomain.EventSourcing.Sagas.FutureEvents;
5+
using NUnit.Framework;
6+
7+
namespace GridDomain.Tests.Serialization
8+
{
9+
[TestFixture]
10+
class Aggregate_can_be_used_for_snapshotting_by_defaut
11+
{
12+
private TestAggregate _restoredAggregate;
13+
private TestAggregate _aggregate;
14+
15+
class TestAggregate : Aggregate
16+
{
17+
public int Value { get; private set; }
18+
19+
public TestAggregate(int value, Guid id):base(id)
20+
{
21+
RaiseEvent(new TestAggregateCreatedEvent(id, value));
22+
}
23+
24+
private void Apply(TestAggregateCreatedEvent e)
25+
{
26+
Id = e.SourceId;
27+
Value = e.Value;
28+
}
29+
30+
internal class TestAggregateCreatedEvent : DomainEvent
31+
{
32+
public int Value { get; }
33+
34+
public TestAggregateCreatedEvent(Guid id, int value) : base(id)
35+
{
36+
Value = value;
37+
}
38+
}
39+
}
40+
41+
[OneTimeSetUp]
42+
public void Aggregate_by_default_can_be_saved_as_IMemento_for_snapshot()
43+
{
44+
_aggregate = new TestAggregate(1,Guid.NewGuid());
45+
var snapshot = ((IAggregate)_aggregate).GetSnapshot();
46+
var factory = new AggregateFactory();
47+
_restoredAggregate = factory.Build<TestAggregate>(_aggregate.Id, snapshot);
48+
}
49+
50+
[Test]
51+
public void Ids_are_equal()
52+
{
53+
Assert.AreEqual(_aggregate.Id, _restoredAggregate.Id);
54+
}
55+
56+
[Test]
57+
public void Restored_aggregate_is_not_null()
58+
{
59+
Assert.NotNull(_restoredAggregate);
60+
}
61+
62+
[Test]
63+
public void Restored_aggregate_uncommitted_events_are_empty()
64+
{
65+
CollectionAssert.IsEmpty(((IAggregate)_restoredAggregate).GetUncommittedEvents());
66+
}
67+
68+
[Test]
69+
public void Restored_aggregate_state_is_equal_to_origin()
70+
{
71+
Assert.AreEqual(_aggregate.Value, _restoredAggregate.Value);
72+
}
73+
}
74+
75+
}

GridDomain.EventSourcing.Sagas/FutureEvents/Aggregate.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace GridDomain.EventSourcing.Sagas.FutureEvents
1010
{
11-
public class Aggregate : AggregateBase
11+
public class Aggregate : AggregateBase, IMemento
1212
{
1313
private static readonly AggregateFactory Factory = new AggregateFactory();
1414
public static T Empty<T>(Guid id) where T : IAggregate
@@ -117,5 +117,22 @@ private void DeleteFutureEvent(Guid futureEventId)
117117

118118
#endregion
119119

120+
// Only for simple implementation
121+
Guid IMemento.Id
122+
{
123+
get { return Id; }
124+
set { Id = value; }
125+
}
126+
127+
int IMemento.Version
128+
{
129+
get { return Version; }
130+
set { Version = value; }
131+
}
132+
133+
protected override IMemento GetSnapshot()
134+
{
135+
return this;
136+
}
120137
}
121138
}

GridDomain.EventSourcing/AggregateFactory.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@ public IAggregate Build(Type type, Guid id, IMemento snapshot)
1717
return snapshot == null ? Build(type, id) : BuildFromSnapshot(type, id, snapshot);
1818
}
1919

20+
//default convention: Aggregate is implementing IMemento itself
2021
protected virtual IAggregate BuildFromSnapshot(Type type, Guid id, IMemento snapshot)
2122
{
22-
return Build(type, id);
23+
var aggregate = snapshot as IAggregate;
24+
if (aggregate == null)
25+
throw new InvalidDefaultMementoException(type, id, snapshot);
26+
aggregate.ClearUncommittedEvents();
27+
return aggregate;
2328
}
2429

2530
public IAggregate Build(Type type, Guid id)

GridDomain.EventSourcing/GridDomain.EventSourcing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Compile Include="Adapters\IDomainEventAdapter.cs" />
6666
<Compile Include="Adapters\IEventAdapter.cs" />
6767
<Compile Include="Adapters\VersionedTypeSerializationBinder.cs" />
68+
<Compile Include="InvalidDefaultMementoException.cs" />
6869
<Compile Include="ISourcedEvent.cs" />
6970
<Compile Include="Properties\AssemblyInfo.cs" />
7071
<Compile Include="Adapters\ObjectAdapter.cs" />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using CommonDomain;
3+
4+
namespace GridDomain.EventSourcing
5+
{
6+
public class InvalidDefaultMementoException : Exception
7+
{
8+
public IMemento Snapshot { get; set; }
9+
public Type Type { get; set; }
10+
public Guid Id { get; set; }
11+
12+
public InvalidDefaultMementoException(Type type, Guid id, IMemento snapshot)
13+
:base("Aggregate cannot be constructed from snapshot by default convention, memento is not IAggregate")
14+
{
15+
Snapshot = snapshot;
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)