Skip to content

Projections#1

Merged
jsedlak merged 27 commits into
mainfrom
projections
Oct 22, 2025
Merged

Projections#1
jsedlak merged 27 commits into
mainfrom
projections

Conversation

@jsedlak

@jsedlak jsedlak commented Sep 22, 2025

Copy link
Copy Markdown
Owner

Adds Projections

Full documentation in projections.md
Task list is in projections.tasks.md

@jsedlak jsedlak requested a review from Copilot September 22, 2025 00:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive projections feature for the Strata Framework, adding asynchronous event processing capabilities to event-sourced grains. The implementation provides two projection approaches: Host Managed Projections (using Orleans OneWay calls) and Projection Managed Grains (using Orleans streams).

  • Core projection infrastructure with type-safe interfaces and configuration options
  • Host Managed Projections via ProjectionGrain with internal queuing and worker threads
  • Projection Managed Grains via EventRecipientGrain with stream-based event processing

Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Strata/Projections/IProjection.cs Core generic projection interface defining Handle method
src/Strata/Projections/IProjectionGrain.cs Orleans grain interface for projection processing with OneWay calls
src/Strata/Projections/ProjectionGrain.cs Main projection grain implementation with internal queue and worker threads
src/Strata/Projections/ProjectionOptions.cs Configuration options with validation attributes
src/Strata/Projections/ProjectionRegistry.cs Registry for managing projection type registrations and mappings
src/Strata/Projections/ProjectionStateManager.cs State management for stateful projections with serialization support
src/Strata/Projections/StreamEventProcessor.cs Stream event processing with reflection-based method routing
src/Strata/Projections/EventRecipientGrain.cs Base class for stream-based projection grains
src/Strata/Projections/GrainExtensions.cs Extension methods for registering projections with EventSourcedGrain
src/Strata/Projections/ServiceCollectionExtensions.cs Dependency injection configuration extensions
src/Strata/EventSourcedGrain.cs Integration of projection processing into event raising
tests and examples Comprehensive test coverage and usage examples

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

/// <returns>A collection of projection IDs.</returns>
public IEnumerable<string> GetAllProjectionIds()
{
return _stateCache.Keys.ToList();

Copilot AI Sep 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ToList() call creates an unnecessary copy of the keys collection. Consider returning IEnumerable directly or use _stateCache.Keys.ToArray() if materialization is needed for thread safety.

Suggested change
return _stateCache.Keys.ToList();
return _stateCache.Keys;

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +28
public ProjectionStateManager(ILogger<ProjectionStateManager> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_stateCache = new Dictionary<string, object>();
_stateVersions = new Dictionary<string, int>();
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};

Copilot AI Sep 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider making JsonSerializerOptions configurable through dependency injection or options pattern rather than hardcoding the configuration. This would allow different serialization behaviors for different scenarios.

Suggested change
public ProjectionStateManager(ILogger<ProjectionStateManager> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_stateCache = new Dictionary<string, object>();
_stateVersions = new Dictionary<string, int>();
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
public ProjectionStateManager(
ILogger<ProjectionStateManager> logger,
JsonSerializerOptions jsonOptions)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_stateCache = new Dictionary<string, object>();
_stateVersions = new Dictionary<string, int>();
_jsonOptions = jsonOptions ?? throw new ArgumentNullException(nameof(jsonOptions));

Copilot uses AI. Check for mistakes.
Comment on lines +226 to +227
var projectionGrain = grain.GrainFactory.GetGrain<IProjectionGrain>(
$"{grain.GetGrainId()}_{projectionType.Name}");

Copilot AI Sep 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The grain ID construction using string concatenation creates a magic string format. Consider extracting this to a constant or helper method to ensure consistency across the codebase.

Copilot uses AI. Check for mistakes.
Comment thread src/Strata/EventSourcedGrain.cs Outdated
Comment on lines +49 to +50
// Process projections asynchronously (fire-and-forget)
_ = Task.Run(async () => await ProcessProjectionsAsync(@event));

Copilot AI Sep 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Task.Run for CPU-bound work is appropriate, but consider using ConfigureAwait(false) in the async lambda to avoid capturing synchronization context unnecessarily.

Copilot uses AI. Check for mistakes.

try
{
var instance = Activator.CreateInstance(projectionTypeInfo);

Copilot AI Sep 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using Activator.CreateInstance assumes parameterless constructors. Consider supporting dependency injection by integrating with the service provider to create projection instances with their dependencies.

Copilot uses AI. Check for mistakes.
@jsedlak jsedlak merged commit 9a20008 into main Oct 22, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants