-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: abstractions for projections * chore: add some doc comments * wip: checkpoint * refactor: rebase on feat/better-reducer * refactor: not nullable, will never be null * bugfix: add missing base class * Delete IStatement.cs * checkpoint Co-Authored-By: watterssn <297749+watterssn@users.noreply.github.com> * chore: todos for better way to update the cache * refactor: account for changes from #29 * feat: finish implementations first pass * refactor: rename TransactionEntity to TestEntity (no such thing as non-transaction entity) * refactor: rename variables to entity snapshots to be distinct from single entity projection snapshots * test: add entity snapshot subscriber for all entity snapshot tests * bugfix: didn't mean to commit this yet * refactor: better name * test: generalize snapshot tests to work for projection snapshots as well * chore: store entity type * chore: add report generator for easily drilling down through coverage * chore: clean up coverage a little * refactor: remove unused method * wip: coverage test * test: add coverage for projection repository/subscriber Co-authored-by: watterssn <297749+watterssn@users.noreply.github.com>
- Loading branch information
1 parent
4c4eb71
commit 80659fe
Showing
42 changed files
with
1,137 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"version": 1, | ||
"isRoot": true, | ||
"tools": { | ||
"dotnet-reportgenerator-globaltool": { | ||
"version": "5.1.2", | ||
"commands": [ | ||
"reportgenerator" | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/sh | ||
rm -rf ./TestResults ./CoverageReport | ||
dotnet tool restore | ||
dotnet restore EntityDb.sln --locked-mode | ||
#dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover | ||
#dotnet reportgenerator -reports:"./TestResults/**/coverage.opencover.xml" -targetdir:"CoverageReport" -reporttypes:Html -license:"ew0KICAiTGljZW5zZSI6IHsNCiAgICAiSWQiOiAiZDZkYjQ2NTYtMjUyMS00MjNlLWE0MTgtZmU2NjJiNDZiMDk3IiwNCiAgICAiTG9naW4iOiAidGhlLWF2aWQtZW5naW5lZXIiLA0KICAgICJOYW1lIjogIkNocmlzIFBoaWxpcHMiLA0KICAgICJFbWFpbCI6IG51bGwsDQogICAgIkxpY2Vuc2VUeXBlIjogIlBybyIsDQogICAgIklzc3VlZEF0IjogIjIwMjItMDMtMTlUMDA6MjA6MTYuNDQ2OTg3MVoiDQogIH0sDQogICJTaWduYXR1cmUiOiAidmZtTldaN2VVeTZjbVpaUFVMS2hCUzBSYVAzNVFjdlNRMWdEbU1PWTFhenlsTlpUVDNEUjl0bWptZDc3RTZVaTAycFZOWDA2YTdCMzc4ZHV6NHZ0TFdFOWg2VDNOMUhXWTJwXHUwMDJCN0FYbVlqc1x1MDAyQmdTbVNud0tueWFiNjJ5dHhLd3Y0TWdLbjh2OHF4aXlCOVBJMGV4YkJPTE11VXAwWVQ4WDc0YW9teWhEam5aWm9kbXhrN05zYzllQTBxRnBFaEZ0QkEzRzNFSWdcdTAwMkJNNHVPb002VEtNMERaV2g1NGFsVHloU25QS2ZNSjRuMWp1OWxGaHlHektDaXhOcUJhTk5LSVl6UnFEWmhxbEpYSGFBQmM4RnNGVlNWVnBwQTN6dUR3aGxxYmxHZGhOVFpTM0w2Y1FyNXNrMS9RV1ZpNDYwbXFnVWNlSVx1MDAyQmI3R2d5eFFCM3AyUG9ZQ1FFREE9PSINCn0=" | ||
dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults | ||
dotnet reportgenerator -reports:"./TestResults/**/coverage.cobertura.xml" -targetdir:"CoverageReport" -reporttypes:Html -license:"ew0KICAiTGljZW5zZSI6IHsNCiAgICAiSWQiOiAiZDZkYjQ2NTYtMjUyMS00MjNlLWE0MTgtZmU2NjJiNDZiMDk3IiwNCiAgICAiTG9naW4iOiAidGhlLWF2aWQtZW5naW5lZXIiLA0KICAgICJOYW1lIjogIkNocmlzIFBoaWxpcHMiLA0KICAgICJFbWFpbCI6IG51bGwsDQogICAgIkxpY2Vuc2VUeXBlIjogIlBybyIsDQogICAgIklzc3VlZEF0IjogIjIwMjItMDMtMTlUMDA6MjA6MTYuNDQ2OTg3MVoiDQogIH0sDQogICJTaWduYXR1cmUiOiAidmZtTldaN2VVeTZjbVpaUFVMS2hCUzBSYVAzNVFjdlNRMWdEbU1PWTFhenlsTlpUVDNEUjl0bWptZDc3RTZVaTAycFZOWDA2YTdCMzc4ZHV6NHZ0TFdFOWg2VDNOMUhXWTJwXHUwMDJCN0FYbVlqc1x1MDAyQmdTbVNud0tueWFiNjJ5dHhLd3Y0TWdLbjh2OHF4aXlCOVBJMGV4YkJPTE11VXAwWVQ4WDc0YW9teWhEam5aWm9kbXhrN05zYzllQTBxRnBFaEZ0QkEzRzNFSWdcdTAwMkJNNHVPb002VEtNMERaV2g1NGFsVHloU25QS2ZNSjRuMWp1OWxGaHlHektDaXhOcUJhTk5LSVl6UnFEWmhxbEpYSGFBQmM4RnNGVlNWVnBwQTN6dUR3aGxxYmxHZGhOVFpTM0w2Y1FyNXNrMS9RV1ZpNDYwbXFnVWNlSVx1MDAyQmI3R2d5eFFCM3AyUG9ZQ1FFREE9PSINCn0=" | ||
open CoverageReport/index.html |
36 changes: 36 additions & 0 deletions
36
src/EntityDb.Abstractions/Projections/IProjectionRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using EntityDb.Abstractions.Disposables; | ||
using EntityDb.Abstractions.Snapshots; | ||
using EntityDb.Abstractions.Transactions; | ||
using EntityDb.Abstractions.ValueObjects; | ||
using System.Threading.Tasks; | ||
|
||
namespace EntityDb.Abstractions.Projections; | ||
|
||
/// <summary> | ||
/// Encapsulates the snapshot repository for a projection. | ||
/// </summary> | ||
/// <typeparam name="TProjection">The type of the projection.</typeparam> | ||
public interface IProjectionRepository<TProjection> : IDisposableResource | ||
{ | ||
/// <summary> | ||
/// The strategy for mapping between projection id and entity id. | ||
/// </summary> | ||
IProjectionStrategy<TProjection> ProjectionStrategy { get; } | ||
|
||
/// <summary> | ||
/// The backing transaction repository. | ||
/// </summary> | ||
ITransactionRepository TransactionRepository { get; } | ||
|
||
/// <summary> | ||
/// The backing snapshot repository. | ||
/// </summary> | ||
ISnapshotRepository<TProjection> SnapshotRepository { get; } | ||
|
||
/// <summary> | ||
/// Returns the current state of a <typeparamref name="TProjection" />. | ||
/// </summary> | ||
/// <param name="projectionId">The id of the projection.</param> | ||
/// <returns>The current state of a <typeparamref name="TProjection" />.</returns> | ||
Task<TProjection> GetCurrent(Id projectionId); | ||
} |
19 changes: 19 additions & 0 deletions
19
src/EntityDb.Abstractions/Projections/IProjectionRepositoryFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System.Threading.Tasks; | ||
|
||
namespace EntityDb.Abstractions.Projections; | ||
|
||
/// <summary> | ||
/// Represents a type used to create instances of <see cref="IProjectionRepository{TProjection}" /> | ||
/// </summary> | ||
/// <typeparam name="TProjection">The type of projection managed by the <see cref="IProjectionRepository{TProjection}" />.</typeparam> | ||
public interface IProjectionRepositoryFactory<TProjection> | ||
{ | ||
/// <summary> | ||
/// Create a new instance of <see cref="IProjectionRepository{TProjection}" /> | ||
/// </summary> | ||
/// <param name="transactionSessionOptionsName">The agent's use case for the transaction repository.</param> | ||
/// <param name="snapshotSessionOptionsName">The agent's use case for the snapshot repository.</param> | ||
/// <returns>A new instance of <see cref="IProjectionRepository{TProjection}" />.</returns> | ||
Task<IProjectionRepository<TProjection>> CreateRepository(string transactionSessionOptionsName, | ||
string snapshotSessionOptionsName); | ||
} |
26 changes: 26 additions & 0 deletions
26
src/EntityDb.Abstractions/Projections/IProjectionStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using EntityDb.Abstractions.ValueObjects; | ||
using System.Threading.Tasks; | ||
|
||
namespace EntityDb.Abstractions.Projections; | ||
|
||
/// <summary> | ||
/// Represents a type that can map a projection it to a set of entity ids. | ||
/// </summary> | ||
/// <typeparam name="TProjection"></typeparam> | ||
public interface IProjectionStrategy<in TProjection> | ||
{ | ||
/// <summary> | ||
/// Map a projection id to a set of entity ids. | ||
/// </summary> | ||
/// <param name="projectionId">The id of the projection.</param> | ||
/// <param name="projectionSnapshot">A snapshot of the projection, if one exists. (This can be used to avoid running a query, if one were necessary.)</param> | ||
/// <returns>The set of entity ids to query for running the projection.</returns> | ||
Task<Id[]> GetEntityIds(Id projectionId, TProjection projectionSnapshot); | ||
|
||
/// <summary> | ||
/// Map an entity id to a set of projection ids. | ||
/// </summary> | ||
/// <param name="entityId">The id of th entity.</param> | ||
/// <returns>The set of projection ids to query for running the projection.</returns> | ||
Task<Id[]> GetProjectionIds(Id entityId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
src/EntityDb.Common/Disposables/DisposableResourceBaseClass.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
src/EntityDb.Common/Disposables/DisposableResourceBaseRecord.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using EntityDb.Abstractions.Annotations; | ||
using EntityDb.Abstractions.ValueObjects; | ||
|
||
namespace EntityDb.Common.Projections; | ||
|
||
/// <summary> | ||
/// Provides basic functionality for the common implementations. | ||
/// </summary> | ||
/// <typeparam name="TProjection"></typeparam> | ||
public interface IProjection<out TProjection> | ||
{ | ||
/// <summary> | ||
/// Creates a new instance of a <typeparamref name="TProjection" />. | ||
/// </summary> | ||
/// <param name="projectionId">The id of the entity.</param> | ||
/// <returns>A new instance of <typeparamref name="TProjection" />.</returns> | ||
abstract static TProjection Construct(Id projectionId); | ||
|
||
/// <summary> | ||
/// Returns the current version number of an entity. | ||
/// </summary> | ||
/// <returns></returns> | ||
VersionNumber GetEntityVersionNumber(Id entityId); | ||
|
||
/// <summary> | ||
/// Returns a new <typeparamref name="TProjection"/> that incorporates the commands for a particular entity id. | ||
/// </summary> | ||
/// <param name="annotatedCommands">The annotated commands.</param> | ||
/// <returns>A new <typeparamref name="TProjection"/> that incorporates <paramref name="annotatedCommands"/>.</returns> | ||
TProjection Reduce(params IEntityAnnotation<object>[] annotatedCommands); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using EntityDb.Abstractions.Projections; | ||
using EntityDb.Abstractions.Snapshots; | ||
using EntityDb.Abstractions.Transactions; | ||
using EntityDb.Abstractions.ValueObjects; | ||
using EntityDb.Common.Disposables; | ||
using EntityDb.Common.Queries; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace EntityDb.Common.Projections; | ||
|
||
internal sealed class ProjectionRepository<TProjection> : DisposableResourceBaseClass, IProjectionRepository<TProjection> | ||
where TProjection : IProjection<TProjection> | ||
{ | ||
public IProjectionStrategy<TProjection> ProjectionStrategy { get; } | ||
public ITransactionRepository TransactionRepository { get; } | ||
public ISnapshotRepository<TProjection> SnapshotRepository { get; } | ||
|
||
public ProjectionRepository | ||
( | ||
IProjectionStrategy<TProjection> projectionStrategy, | ||
ISnapshotRepository<TProjection> snapshotRepository, | ||
ITransactionRepository transactionRepository | ||
) | ||
{ | ||
ProjectionStrategy = projectionStrategy; | ||
TransactionRepository = transactionRepository; | ||
SnapshotRepository = snapshotRepository; | ||
} | ||
|
||
public async Task<TProjection> GetCurrent(Id projectionId) | ||
{ | ||
var projection = await SnapshotRepository.GetSnapshot(projectionId) ?? TProjection.Construct(projectionId); | ||
|
||
var entityIds = await ProjectionStrategy.GetEntityIds(projectionId, projection); | ||
|
||
if (entityIds.Length == 0) | ||
{ | ||
return projection; | ||
} | ||
|
||
foreach (var entityId in entityIds) | ||
{ | ||
var entityVersionNumber = projection.GetEntityVersionNumber(entityId); | ||
|
||
var commandQuery = new GetCurrentEntityQuery(entityId, entityVersionNumber); | ||
|
||
var annotatedCommands = await TransactionRepository.GetAnnotatedCommands(commandQuery); | ||
|
||
projection = projection.Reduce(annotatedCommands); | ||
} | ||
|
||
return projection; | ||
} | ||
|
||
public static ProjectionRepository<TProjection> Create | ||
( | ||
IServiceProvider serviceProvider, | ||
ITransactionRepository transactionRepository, | ||
ISnapshotRepository<TProjection> snapshotRepository | ||
) | ||
{ | ||
return ActivatorUtilities.CreateInstance<ProjectionRepository<TProjection>>(serviceProvider, | ||
transactionRepository, snapshotRepository); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/EntityDb.Common/Projections/ProjectionRepositoryFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using EntityDb.Abstractions.Projections; | ||
using EntityDb.Abstractions.Snapshots; | ||
using EntityDb.Abstractions.Transactions; | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace EntityDb.Common.Projections; | ||
|
||
internal class ProjectionRepositoryFactory<TProjection> : IProjectionRepositoryFactory<TProjection> | ||
where TProjection : IProjection<TProjection> | ||
{ | ||
private readonly IServiceProvider _serviceProvider; | ||
private readonly ITransactionRepositoryFactory _transactionRepositoryFactory; | ||
private readonly ISnapshotRepositoryFactory<TProjection> _snapshotRepositoryFactory; | ||
|
||
public ProjectionRepositoryFactory | ||
( | ||
IServiceProvider serviceProvider, | ||
ITransactionRepositoryFactory transactionRepositoryFactory, | ||
ISnapshotRepositoryFactory<TProjection> snapshotRepositoryFactory | ||
) | ||
{ | ||
_serviceProvider = serviceProvider; | ||
_transactionRepositoryFactory = transactionRepositoryFactory; | ||
_snapshotRepositoryFactory = snapshotRepositoryFactory; | ||
} | ||
|
||
public async Task<IProjectionRepository<TProjection>> CreateRepository(string transactionSessionOptionsName, string snapshotSessionOptionsName) | ||
{ | ||
var transactionRepository = | ||
await _transactionRepositoryFactory.CreateRepository(transactionSessionOptionsName); | ||
|
||
var snapshotRepository = | ||
await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptionsName); | ||
|
||
return ProjectionRepository<TProjection>.Create(_serviceProvider, | ||
transactionRepository, snapshotRepository); | ||
} | ||
} |
Oops, something went wrong.