Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions Deveel.Repository.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.Manager.X
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.Manager.DynamicLinq", "src\Deveel.Repository.Manager.DynamicLinq\Deveel.Repository.Manager.DynamicLinq.csproj", "{638851EF-B000-490C-9035-A962279A3E9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deveel.Repository.Manager.EasyCaching", "src\Deveel.Repository.Manager.EasyCaching\Deveel.Repository.Manager.EasyCaching.csproj", "{84D55BE2-7DAD-4CA3-A3E2-EFB4D41FA3CA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.Manager.EasyCaching", "src\Deveel.Repository.Manager.EasyCaching\Deveel.Repository.Manager.EasyCaching.csproj", "{84D55BE2-7DAD-4CA3-A3E2-EFB4D41FA3CA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deveel.Repository.Manager.EasyCaching.XUnit", "test\Deveel.Repository.Manager.EasyCaching.XUnit\Deveel.Repository.Manager.EasyCaching.XUnit.csproj", "{F47C196B-758D-4C05-8918-FB63C61649EB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.Manager.EasyCaching.XUnit", "test\Deveel.Repository.Manager.EasyCaching.XUnit\Deveel.Repository.Manager.EasyCaching.XUnit.csproj", "{F47C196B-758D-4C05-8918-FB63C61649EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.MartenDb", "src\Deveel.Repository.MartenDb\Deveel.Repository.MartenDb.csproj", "{96284897-30DC-4F27-866C-355F06D02DFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.MartenDb.XUnit", "test\Deveel.Repository.MartenDb.XUnit\Deveel.Repository.MartenDb.XUnit.csproj", "{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Repository.Aggregate.Model", "src\Deveel.Repository.Aggregate.Model\Deveel.Repository.Aggregate.Model.csproj", "{D7403F7B-AD31-4F06-876B-1D97103B931A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -126,6 +132,18 @@ Global
{F47C196B-758D-4C05-8918-FB63C61649EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F47C196B-758D-4C05-8918-FB63C61649EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F47C196B-758D-4C05-8918-FB63C61649EB}.Release|Any CPU.Build.0 = Release|Any CPU
{96284897-30DC-4F27-866C-355F06D02DFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96284897-30DC-4F27-866C-355F06D02DFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96284897-30DC-4F27-866C-355F06D02DFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96284897-30DC-4F27-866C-355F06D02DFB}.Release|Any CPU.Build.0 = Release|Any CPU
{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2}.Release|Any CPU.Build.0 = Release|Any CPU
{D7403F7B-AD31-4F06-876B-1D97103B931A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7403F7B-AD31-4F06-876B-1D97103B931A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7403F7B-AD31-4F06-876B-1D97103B931A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7403F7B-AD31-4F06-876B-1D97103B931A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -148,6 +166,9 @@ Global
{638851EF-B000-490C-9035-A962279A3E9B} = {2860FD4D-510F-43C8-870E-5559B90D0CAD}
{84D55BE2-7DAD-4CA3-A3E2-EFB4D41FA3CA} = {2860FD4D-510F-43C8-870E-5559B90D0CAD}
{F47C196B-758D-4C05-8918-FB63C61649EB} = {50434E05-0F21-4871-AFB3-A483CEE4A300}
{96284897-30DC-4F27-866C-355F06D02DFB} = {2860FD4D-510F-43C8-870E-5559B90D0CAD}
{8CAC793C-A886-42C8-B754-7E5F7D8DE1F2} = {50434E05-0F21-4871-AFB3-A483CEE4A300}
{D7403F7B-AD31-4F06-876B-1D97103B931A} = {2860FD4D-510F-43C8-870E-5559B90D0CAD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {01FD9B16-84B3-4D99-80C1-11B2F3D65B56}
Expand Down
73 changes: 73 additions & 0 deletions src/Deveel.Repository.Aggregate.Model/Data/Aggregate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Deveel.Data {
/// <summary>
/// Provides a base implementation of an object
/// that aggregates a stream of events to form
/// its state.
/// </summary>
public abstract class Aggregate {
/// <summary>
/// Constructs the aggregate from the initial set of
/// committed events.
/// </summary>
/// <param name="committedEvents"></param>
protected Aggregate(IEnumerable<object>? committedEvents = null) {
Events = new EventCollection(this, committedEvents);

foreach (var @event in Events.Committed) {
ApplyEvent(@event);
}
}

/// <summary>
/// Gets the stream of events that are applied to
/// the aggregate.
/// </summary>
public EventCollection Events { get; }

/// <summary>
/// Gets the version of the aggregate.
/// </summary>
/// <remarks>
/// The default implementation returns the total number
/// of events in the stream.
/// </remarks>
[Version]
public virtual int Version => Events.Count;

/// <summary>
/// Applies the given event to the aggregate, changing
/// its state and incrementing the version.
/// </summary>
/// <param name="event">
/// The event to apply to the aggregate.
/// </param>
public void Apply(object @event) {
ArgumentNullException.ThrowIfNull(@event, nameof(@event));

ApplyEvent(@event);

Events.Add(@event);
}

/// <summary>
/// When overridden in a derived class, applies the given
/// event to the aggregate, changing its state.
/// </summary>
/// <param name="event">
/// The event to apply to the aggregate.
/// </param>
protected virtual void ApplyEvent(object @event) {
}

/// <summary>
/// Commits the current stream of events to the aggregate.
/// </summary>
public void Commit() => Events.Commit();

/// <summary>
/// Rolls back the current stream of uncommitted events
/// to the aggregate.
/// </summary>
public void Rollback() => Events.Clear();
}
}
143 changes: 143 additions & 0 deletions src/Deveel.Repository.Aggregate.Model/Data/EventCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System.Collections;

namespace Deveel.Data {
/// <summary>
/// A collection of events that are applied to an aggregate.
/// </summary>
public sealed class EventCollection : IReadOnlyCollection<object> {
private readonly List<EventState> events;

/// <summary>
/// Constructs the collection from the initial
/// set of committed events.
/// </summary>
/// <param name="aggregate">
/// The aggregate that the events are applied to.
/// </param>
/// <param name="committedEvents">
/// The initial set of events that are committed to
/// form the aggregate.
/// </param>
internal EventCollection(Aggregate aggregate, IEnumerable<object>? committedEvents = null) {
ArgumentNullException.ThrowIfNull(aggregate, nameof(aggregate));

Aggregate = aggregate;

events = new List<EventState>();

if (committedEvents != null) {
foreach (var @event in committedEvents) {
events.Add(new EventState(@event, true));
}
}
}

/// <summary>
/// Gets the aggregate that the events are applied to.
/// </summary>
public Aggregate Aggregate { get; }

/// <summary>
/// Gets the total number of events in the collection.
/// </summary>
public int Count {
get {
lock (events) {
return events.Count;
}
}
}

/// <inheritdoc/>
public IReadOnlyList<object> Committed {
get {
lock (events) {
return events.Where(x => x.IsCommitted).Select(x => x.Event).ToArray();
}
}
}

/// <inheritdoc/>
public IReadOnlyList<object> Uncommitted {
get {
lock (events) {
return events.Where(x => !x.IsCommitted).Select(x => x.Event).ToArray();
}
}
}

/// <summary>
/// Adds a new event to the collection as uncommitted.
/// </summary>
/// <param name="event">
/// The event to add to the collection.
/// </param>
/// <remarks>
/// The event is added to the collection as uncommitted,
/// that means that it is not yet part of the aggregate
/// and that can be removed or cleared.
/// </remarks>
internal void Add(object @event) {
lock (events) {
events.Add(new EventState(@event, false));
}
}

/// <summary>
/// Clears all the uncommitted events from the collection.
/// </summary>
internal void Clear() {
lock (events) {
for (var i = events.Count - 1; i >= 0; i--) {
if (!events[i].IsCommitted)
events.RemoveAt(i);
}
}
}

internal void Commit() {
lock (events) {
for (var i = events.Count - 1; i >= 0; i--) {
if (!events[i].IsCommitted)
events[i] = new EventState(events[i].Event, true);
}
}
}

/// <summary>
/// Checks if the given event is contained in the collection.
/// </summary>
/// <param name="event">
/// The event to check.
/// </param>
/// <returns>
/// Returns <c>true</c> if the event is contained in the
/// collection, otherwise <c>false</c>.
/// </returns>
public bool Contains(object @event) {
lock (events) {
return events.Any(x => x.Event.Equals(@event));
}
}

/// <inheritdoc/>
public IEnumerator<object> GetEnumerator() {
lock (events) {
return events.Select(x => x.Event).GetEnumerator();
}
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

private readonly struct EventState {
internal EventState(object @event, bool isCommitted) {
Event = @event;
IsCommitted = isCommitted;
}

public object Event { get; }

public bool IsCommitted { get; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Deveel.Data {
/// <summary>
/// An attribute that is used to identify a property of an
/// aggregate that is used to obtain its version.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class VersionAttribute : Attribute {
}
}
Loading