Skip to content

Commit

Permalink
Added IStore and IStoreRepository to store generic objects (#64)
Browse files Browse the repository at this point in the history
Co-authored-by: Dmitriy Melnychenko <dmitriy.melnichenko@techfabric.com>
  • Loading branch information
scand1n and Dmitriy Melnychenko authored Aug 17, 2023
1 parent 2ecc755 commit 8283cce
Show file tree
Hide file tree
Showing 27 changed files with 510 additions and 310 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [ "main" ]

env:
PACKAGE_VERSION: 0.1.4
PACKAGE_VERSION: 0.1.5

jobs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ string processorName
var cosmosClient = new CosmosClient(connectionString, cosmosClientOptions);
var eventStore = new CosmosDbEventStore(cosmosClient, databaseId, eventsContainerId, itemsContainerId);
var eventStore = new CosmosDbEventStore(cosmosClient, databaseId, eventsContainerId);
eventStore.Initialize().Wait();
var eventStoreObserver = new CosmosDbEventStoreChangeFeedObserver(
Expand All @@ -45,6 +45,11 @@ string processorName
return new AggregateRepositoryFactory(eventStore);
});

var storeRepository = new StoreRepository(
new CosmosDbStore(connectionString, cosmosClientOptions, databaseId, itemsContainerId)
);
services.AddScoped<IStoreRepository>(sp => storeRepository);

return new EventSourcingBuilder
{
Expand All @@ -56,11 +61,10 @@ public static IEventSourcingBuilder AddCosmosDbEventStore(
this IServiceCollection services,
CosmosClient client,
string databaseId,
string eventsContainerId,
string itemsContainerId
string eventsContainerId
)
{
var eventStore = new CosmosDbEventStore(client, databaseId, eventsContainerId, itemsContainerId);
var eventStore = new CosmosDbEventStore(client, databaseId, eventsContainerId);
eventStore.Initialize().Wait();

return new EventSourcingBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static IEventSourcingBuilder AddInMemoryEventStore(
services.AddScoped<IEventStore>(
(sp) =>
{
var eventStore = new InMemoryEventStore(eventsContainer, itemsContainer);
var eventStore = new InMemoryEventStore(eventsContainer);
eventStore.Initialize().Wait();
// add events observer for projections
Expand Down Expand Up @@ -58,6 +58,8 @@ public static IEventSourcingBuilder AddInMemoryEventStore(
}
);

services.AddScoped<IStoreRepository>(sp => new StoreRepository(new InMemoryStore(itemsContainer)));

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class PostgresqlEventSourcingScope
public IEventStore EventStore { get; set; }
public EventsObserver EventsObserver { get; set; }
public ProjectionsEngine? ProjectionsEngine { get; set; }
public IStoreRepository StoreRepository { get; set; }
}

public static class ServiceCollectionExtensions
Expand Down Expand Up @@ -83,6 +84,8 @@ Func<IServiceProvider, IPostgresqlEventStoreConnectionInformationProvider> conne
scope.ProjectionsEngine.StartAsync(connectionInformationProvider.GetConnectionInformation().ConnectionId).GetAwaiter().GetResult();
}
scope.StoreRepository = new StoreRepository(new PostgresqlStore(connectionInformationProvider));
return scope;
}
);
Expand All @@ -105,6 +108,15 @@ Func<IServiceProvider, IPostgresqlEventStoreConnectionInformationProvider> conne
}
);

services.AddScoped<IStoreRepository>(
(sp) =>
{
var eventSourcingScope = sp.GetRequiredService<PostgresqlEventSourcingScope>();
return eventSourcingScope.StoreRepository;
}
);

return builder;
}

Expand All @@ -120,7 +132,7 @@ string eventsTableName
return services.AddPostgresqlEventStore(
eventsConnectionString,
eventsTableName,
string.Concat(eventsTableName, ItemsEventStoreNameSuffix.TableNameSuffix)
ItemsStoreNameDefaults.AddDefaultTableNameSuffix(eventsTableName)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace CloudFabric.EventSourcing.AspNet;
public interface IEventSourcingBuilder
{
IEventStore EventStore { get; set; }

public AggregateRepositoryFactory AggregateRepositoryFactory { get; set; }

IServiceCollection Services { get; set; }
Expand Down
12 changes: 12 additions & 0 deletions CloudFabric.EventSourcing.Domain/IStoreRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace CloudFabric.EventSourcing.Domain;

/// <summary>
/// Repository that provides possibility of storing typed objects,
/// due to requirements to save non 'evented' aggregates items outside of EventStore.
/// </summary>
public interface IStoreRepository
{
Task UpsertItem<T>(string id, string partitionKey, T item, CancellationToken cancellationToken = default);

Task<T?> LoadItem<T>(string id, string partitionKey, CancellationToken cancellationToken = default);
}
33 changes: 33 additions & 0 deletions CloudFabric.EventSourcing.Domain/StoreRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using CloudFabric.EventSourcing.EventStore;

namespace CloudFabric.EventSourcing.Domain;

public class StoreRepository : IStoreRepository
{
private readonly IStore _store;

public StoreRepository(IStore store)
{
_store = store;
}

public async Task UpsertItem<TItem>(string id, string partitionKey, TItem item, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}

await _store.UpsertItem(id, partitionKey, item, cancellationToken);
}

public async Task<TItem?> LoadItem<TItem>(string id, string partitionKey, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}

return await _store.LoadItem<TItem?>(id, partitionKey, cancellationToken);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace CloudFabric.EventSourcing.EventStore.Enums;

public static class ItemsStoreNameDefaults
{
public const string TableNameSuffix = "-item";

public static string AddDefaultTableNameSuffix(string tableName)
=> string.Concat(tableName, TableNameSuffix);
}
4 changes: 0 additions & 4 deletions CloudFabric.EventSourcing.EventStore/IEventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,4 @@ Task<bool> AppendToStreamAsync(
Task DeleteAll(CancellationToken cancellationToken = default);

Task<bool> HardDeleteAsync(Guid streamId, string partitionKey, CancellationToken cancellationToken = default);

Task UpsertItem<T>(string id, string partitionKey, T item, CancellationToken cancellationToken = default);

Task<T?> LoadItem<T>(string id, string partitionKey, CancellationToken cancellationToken = default);
}
12 changes: 12 additions & 0 deletions CloudFabric.EventSourcing.EventStore/IStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace CloudFabric.EventSourcing.EventStore;

public interface IStore
{
Task Initialize(CancellationToken cancellationToken = default);

Task DeleteAll(CancellationToken cancellationToken = default);

Task UpsertItem<T>(string id, string partitionKey, T item, CancellationToken cancellationToken = default);

Task<T?> LoadItem<T>(string id, string partitionKey, CancellationToken cancellationToken = default);
}
22 changes: 11 additions & 11 deletions CloudFabric.EventSourcing.Tests/ItemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
namespace CloudFabric.EventSourcing.Tests;
public abstract class ItemTests
{
protected abstract Task<IEventStore> GetEventStore();
private IEventStore _eventStore;
protected abstract Task<IStore> GetStore();
private IStore _store;

[TestInitialize]
public async Task Initialize()
{
_eventStore = await GetEventStore();
_store = await GetStore();
}

[TestCleanup]
public async Task Cleanup()
{
await _eventStore.DeleteAll();
await _store.DeleteAll();
}

[TestMethod]
Expand All @@ -35,7 +35,7 @@ public async Task SaveItem()
}
};

Func<Task> action = async () => await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
Func<Task> action = async () => await _store.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await action.Should().NotThrowAsync();
}

Expand All @@ -52,9 +52,9 @@ public async Task LoadItem()
}
};

await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await _store.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);

var loadedItem = await _eventStore.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");
var loadedItem = await _store.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");

loadedItem.Id.Should().Be(item.Id);
loadedItem.Name.Should().Be(item.Name);
Expand All @@ -65,7 +65,7 @@ public async Task LoadItem()
[TestMethod]
public async Task LoadItem_NullIfNotFound()
{
var loadedItem = await _eventStore.LoadItem<TestItem>($"{Guid.NewGuid()}", $"{Guid.NewGuid()}");
var loadedItem = await _store.LoadItem<TestItem>($"{Guid.NewGuid()}", $"{Guid.NewGuid()}");

loadedItem.Should().BeNull();
}
Expand All @@ -84,7 +84,7 @@ public async Task UpdateItem()
}
};

await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await _store.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);

string propertyGuid = Guid.NewGuid().ToString();

Expand All @@ -93,10 +93,10 @@ public async Task UpdateItem()
{ propertyGuid, new TestNestedItemClass() }
};

Func<Task> action = async () => await _eventStore.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
Func<Task> action = async () => await _store.UpsertItem($"{item.Id}{item.Name}", $"{item.Id}{item.Name}", item);
await action.Should().NotThrowAsync();

var updatedItem = await _eventStore.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");
var updatedItem = await _store.LoadItem<TestItem>($"{item.Id}{item.Name}", $"{item.Id}{item.Name}");
updatedItem.Properties.ContainsKey(propertyGuid).Should().BeTrue();
}
}
Loading

0 comments on commit 8283cce

Please sign in to comment.