Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- Microsoft dependency injection / options #10

Merged
merged 2 commits into from
Sep 3, 2022
Merged
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
63 changes: 22 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,24 @@ Start a new **Console Application** project called `RecallQuickstart` and select
PM> Install-Package Shuttle.Recall.Sql.Storage
```

Now we'll need select one of the [supported containers](https://shuttle.github.io/shuttle-core/container/shuttle-core-container.html#implementations):
This provides a SQL-based store for doamin events.

```
PM> Install-Package Shuttle.Core.Ninject
```

Since we will be interacting with Sql Server we will be using the [Shuttle.Core.Data](https://shuttle.github.io/shuttle-core/data/shuttle-core-data.html) data access components as well as the `System.Data.Client` package:
> Install the `System.Data.SqlClient` nuget package.

```
PM> Install-Package Shuttle.Core.Data
PM> Install-Package System.Data.Client
```
This will provide a connection to our Sql Server.

Now we'll define the domain event that will represent a state change in the `Name` attribute:

``` c#
```c#
public class Renamed
{
public string Name { get; set; }
}
```

Next we'll create our `Aggregate Root` that will make use of an `EventStream` to save it's states:
Next we'll create our `Aggregate Root`. In a real-world scenario the aggregate in our domain would be something like `Customer`, `Member`, `Invoice`, and so forth. The aggregate will make use of an `EventStream` to save the changes in the state:

``` c#
```c#
using System;
using System.Collections.Generic;

Expand Down Expand Up @@ -84,53 +77,41 @@ Create a new Sql Server database called `RecallQuickstart` to store our events a
%userprofile%\.nuget\packages\shuttle.recall.sql.storage\{version}\scripts\System.Data.SqlClient\EventStoreCreate.sql
```

Add the relevant `connectionString` to the `App.config` file:

``` xml
<configuration>
<connectionStrings>
<add
name="EventStore"
providerName="System.Data.SqlClient"
connectionString="Data Source=.;Initial Catalog=RecallQuickstart;user id=sa;password=Pass!000"
/>
</connectionStrings>
</configuration>
```

Next we'll use event sourcing to store an rehydrate our aggregate root from the `Main()` entry point:

``` c#
using System;
using System.Data.Common;
using System.Data.SqlClient;
using Ninject;
using Microsoft.Extensions.DependencyInjection;
using Shuttle.Core.Data;
using Shuttle.Core.Ninject;
using Shuttle.Recall;
using Shuttle.Recall.Sql.Storage;

namespace RecallQuickstart
{
internal class Program
{
private static void Main()
static void Main(string[] args)
{
DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance);

var container = new NinjectComponentContainer(new StandardKernel());
var services = new ServiceCollection();

services
.AddDataAccess(builder =>
{
builder.AddConnectionString("EventStore", "System.Data.SqlClient",
"Data Source=.;Initial Catalog=RecallQuickstart;user id=sa;password=Pass!000");

// This registers the event store dependencies provided by Shuttle.Recall
container.RegisterEventStore();
builder.Options.DatabaseContextFactory.DefaultConnectionStringName = "EventStore";
})
.AddSqlEventStorage()
.AddEventStore();

// This registers the sql server implementations provided by Shuttle.Recall.Sql.Storage
container.RegisterEventStoreStorage();

// This registers the ado.net components provided by Shuttle.Core.Data
container.RegisterDataAccess();
var provider = services.BuildServiceProvider();

var databaseContextFactory = container.Resolve<IDatabaseContextFactory>();
var store = container.Resolve<IEventStore>();
var databaseContextFactory = provider.GetRequiredService<IDatabaseContextFactory>();
var store = provider.GetRequiredService<IEventStore>();

var id = Guid.NewGuid();

Expand Down
10 changes: 5 additions & 5 deletions Shuttle.Recall.Tests/ProjectionEventProviderFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void Should_be_able_to_use_provider()

eventProcessor.Setup(m => m.GetProjectionAggregation(It.IsAny<Guid>())).Returns(projectionAggregation);

var provider = new ProjectionEventProvider(Options.Create(new EventStoreOptions()), eventProcessor.Object, GetRepository());
var provider = new ProjectionEventProvider(Options.Create(new EventStoreOptions()), eventProcessor.Object, GetQuery());

ProjectionEvent projectionEvent;

Expand Down Expand Up @@ -57,9 +57,9 @@ public void Should_be_able_to_use_provider()
Assert.That(projectionAggregation.IsEmpty, Is.True);
}

private IPrimitiveEventRepository GetRepository()
private IPrimitiveEventQuery GetQuery()
{
var repository = new Mock<IPrimitiveEventRepository>();
var query = new Mock<IPrimitiveEventQuery>();
var events = new List<PrimitiveEvent>();

for (int i = 0; i < 10; i++)
Expand All @@ -70,11 +70,11 @@ private IPrimitiveEventRepository GetRepository()
});
}

repository.SetupSequence(m => m.Fetch(It.IsAny<long>(), It.IsAny<int>(), It.IsAny<IEnumerable<Type>>()))
query.SetupSequence(m => m.Search(It.IsAny<PrimitiveEvent.Specification>()))
.Returns(events)
.Returns(new List<PrimitiveEvent>());

return repository.Object;
return query.Object;
}

public void ProcessEvent(IEventHandlerContext<TestEvent> context)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
using System;
using System.Collections.Generic;
using Shuttle.Core.Contract;
namespace Shuttle.Recall
{
public class EventEnvelopeConfigurator
{
public EventEnvelopeConfigurator()
{
Headers = new List<EnvelopeHeader>();
}
public List<EnvelopeHeader> Headers { get; set; }
public EventEnvelope EventEnvelope(object @event, string encryptionAlgorithm, string compressionAlgorithm)
{
Guard.AgainstNull(@event, nameof(@event));
var result = new EventEnvelope
{
AssemblyQualifiedName = @event.GetType().AssemblyQualifiedName,
EncryptionAlgorithm = encryptionAlgorithm,
CompressionAlgorithm = compressionAlgorithm,
EventDate = DateTime.UtcNow
};
result.Headers.Merge(Headers);
return result;
}
}
using System;
using System.Collections.Generic;
using Shuttle.Core.Contract;

namespace Shuttle.Recall
{
public class EventEnvelopeBuilder
{
public EventEnvelopeBuilder()
{
Headers = new List<EnvelopeHeader>();
}

public List<EnvelopeHeader> Headers { get; set; }

public EventEnvelope EventEnvelope(object @event, string encryptionAlgorithm, string compressionAlgorithm)
{
Guard.AgainstNull(@event, nameof(@event));

var result = new EventEnvelope
{
AssemblyQualifiedName = @event.GetType().AssemblyQualifiedName,
EncryptionAlgorithm = encryptionAlgorithm,
CompressionAlgorithm = compressionAlgorithm,
EventDate = DateTime.UtcNow
};

result.Headers.Merge(Headers);

return result;
}
}
}
11 changes: 5 additions & 6 deletions Shuttle.Recall/EventProcessing/ProjectionEventProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@ namespace Shuttle.Recall
public class ProjectionEventProvider : IProjectionEventProvider
{
private readonly IEventProcessor _eventProcessor;
private readonly IPrimitiveEventRepository _repository;
private readonly IPrimitiveEventQuery _query;
private readonly EventStoreOptions _eventStoreOptions;
private long _sequenceNumberHead;

public ProjectionEventProvider(IOptions<EventStoreOptions> eventStoreOptions, IEventProcessor eventProcessor,
IPrimitiveEventRepository repository)
public ProjectionEventProvider(IOptions<EventStoreOptions> eventStoreOptions, IEventProcessor eventProcessor, IPrimitiveEventQuery query)
{
Guard.AgainstNull(eventStoreOptions, nameof(eventStoreOptions));
Guard.AgainstNull(eventStoreOptions.Value, nameof(eventStoreOptions.Value));
Guard.AgainstNull(eventProcessor, nameof(eventProcessor));
Guard.AgainstNull(repository, nameof(repository));
Guard.AgainstNull(query, nameof(query));

_eventStoreOptions = eventStoreOptions.Value;
_eventProcessor = eventProcessor;
_repository = repository;
_query = query;
}

public ProjectionEvent Get(Projection projection)
Expand All @@ -43,7 +42,7 @@ public ProjectionEvent Get(Projection projection)

if (!projectionAggregation.ContainsPrimitiveEvent(sequenceNumber))
{
foreach (var primitiveEvent in _repository.Fetch(sequenceNumber, _eventStoreOptions.ProjectionEventFetchCount, projectionAggregation.EventTypes))
foreach (var primitiveEvent in _query.Search(new PrimitiveEvent.Specification().WithRange(sequenceNumber, _eventStoreOptions.ProjectionEventFetchCount).AddEventTypes(projectionAggregation.EventTypes)))
{
projectionAggregation.AddPrimitiveEvent(primitiveEvent);

Expand Down
6 changes: 3 additions & 3 deletions Shuttle.Recall/EventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public long Save(EventStream eventStream)
return Save(eventStream, null);
}

public long Save(EventStream eventStream, Action<EventEnvelopeConfigurator> configure)
public long Save(EventStream eventStream, Action<EventEnvelopeBuilder> builder)
{
Guard.AgainstNull(eventStream, nameof(eventStream));

Expand All @@ -64,9 +64,9 @@ public long Save(EventStream eventStream, Action<EventEnvelopeConfigurator> conf

try
{
var configurator = new EventEnvelopeConfigurator();
var configurator = new EventEnvelopeBuilder();

configure?.Invoke(configurator);
builder?.Invoke(configurator);

pipeline.Execute(eventStream, configurator);

Expand Down
2 changes: 1 addition & 1 deletion Shuttle.Recall/EventStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public EventStream(Guid id, int version, IEnumerable<DomainEvent> events, IEvent
public int Version { get; }
public object Snapshot { get; private set; }

public int Count => (_events?.Count() ?? 0) + _appendedEvents.Count;
public int Count => (_events?.Count ?? 0) + _appendedEvents.Count;

public bool IsEmpty => Count == 0;

Expand Down
2 changes: 1 addition & 1 deletion Shuttle.Recall/IEventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public interface IEventStore
EventStream CreateEventStream();
EventStream Get(Guid id);
long Save(EventStream eventStream);
long Save(EventStream eventStream, Action<EventEnvelopeConfigurator> configurator);
long Save(EventStream eventStream, Action<EventEnvelopeBuilder> builder);
void Remove(Guid id);
}
}
10 changes: 10 additions & 0 deletions Shuttle.Recall/IPrimitiveEventQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;

namespace Shuttle.Recall
{
public interface IPrimitiveEventQuery
{
IEnumerable<PrimitiveEvent> Search(PrimitiveEvent.Specification specification);
}
}
1 change: 0 additions & 1 deletion Shuttle.Recall/IPrimitiveEventRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ public interface IPrimitiveEventRepository
{
void Remove(Guid id);
IEnumerable<PrimitiveEvent> Get(Guid id);
IEnumerable<PrimitiveEvent> Fetch(long fromSequenceNumber, int count, IEnumerable<Type> eventTypes);
long Save(PrimitiveEvent primitiveEvent);
long GetSequenceNumber(Guid id);
}
Expand Down
6 changes: 3 additions & 3 deletions Shuttle.Recall/Pipeline/PipelineStateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ public static EventStream GetEventStream(this IState state)
return state.Get<EventStream>(StateKeys.EventStream);
}

public static void SetEventEnvelopeConfigurator(this IState state, EventEnvelopeConfigurator value)
public static void SetEventEnvelopeConfigurator(this IState state, EventEnvelopeBuilder value)
{
state.Replace(StateKeys.EventEnvelopeConfigurator, value);
}

public static EventEnvelopeConfigurator GetEventEnvelopeConfigurator(this IState state)
public static EventEnvelopeBuilder GetEventEnvelopeConfigurator(this IState state)
{
return state.Get<EventEnvelopeConfigurator>(StateKeys.EventEnvelopeConfigurator);
return state.Get<EventEnvelopeBuilder>(StateKeys.EventEnvelopeConfigurator);
}

public static void SetEvents(this IState state, IEnumerable<DomainEvent> value)
Expand Down
4 changes: 2 additions & 2 deletions Shuttle.Recall/Pipeline/Pipelines/SaveEventStreamPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public SaveEventStreamPipeline(IAssembleEventEnvelopesObserver assembleEventEnve
RegisterObserver(eventStreamObserver);
}

public void Execute(EventStream eventStream, EventEnvelopeConfigurator configurator)
public void Execute(EventStream eventStream, EventEnvelopeBuilder builder)
{
Guard.AgainstNull(eventStream, nameof(eventStream));

State.SetEventStream(eventStream);
State.SetEventEnvelopeConfigurator(configurator);
State.SetEventEnvelopeConfigurator(builder);

Execute();
}
Expand Down
Loading