106 lines
3.5 KiB
C#
106 lines
3.5 KiB
C#
using System;
|
|
using Svrnty.CQRS.Events.Abstractions.Subscriptions;
|
|
using Svrnty.Sample.Events;
|
|
using Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Svrnty.CQRS.Events.Abstractions.Projections;
|
|
|
|
namespace Svrnty.Sample.Projections;
|
|
|
|
/// <summary>
|
|
/// Projection that builds user statistics from UserAddedEvent and UserRemovedEvent.
|
|
/// Demonstrates dynamic projection handling multiple event types.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This projection maintains a simple in-memory read model tracking:
|
|
/// - Total users added
|
|
/// - Total users removed
|
|
/// - Current user count
|
|
/// - Last user details
|
|
/// </para>
|
|
/// <para>
|
|
/// In production, this would typically update a database, cache, or other persistent store.
|
|
/// </para>
|
|
/// </remarks>
|
|
public sealed class UserStatisticsProjection : IDynamicProjection, IResettableProjection
|
|
{
|
|
private readonly UserStatistics _statistics;
|
|
private readonly ILogger<UserStatisticsProjection> _logger;
|
|
|
|
public UserStatisticsProjection(
|
|
UserStatistics statistics,
|
|
ILogger<UserStatisticsProjection> logger)
|
|
{
|
|
_statistics = statistics ?? throw new ArgumentNullException(nameof(statistics));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current statistics (for querying).
|
|
/// </summary>
|
|
public UserStatistics Statistics => _statistics;
|
|
|
|
/// <inheritdoc />
|
|
public Task HandleAsync(ICorrelatedEvent @event, CancellationToken cancellationToken = default)
|
|
{
|
|
switch (@event)
|
|
{
|
|
case UserAddedEvent added:
|
|
return HandleUserAddedAsync(added, cancellationToken);
|
|
|
|
case UserRemovedEvent removed:
|
|
return HandleUserRemovedAsync(removed, cancellationToken);
|
|
|
|
default:
|
|
// Unknown event type - skip
|
|
_logger.LogWarning("Received unknown event type: {EventType}", @event.GetType().Name);
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task ResetAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
_logger.LogInformation("Resetting user statistics projection");
|
|
|
|
_statistics.TotalUsersAdded = 0;
|
|
_statistics.TotalUsersRemoved = 0;
|
|
_statistics.LastUserId = 0;
|
|
_statistics.LastUserName = string.Empty;
|
|
_statistics.LastUserEmail = string.Empty;
|
|
_statistics.LastUpdated = DateTimeOffset.MinValue;
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task HandleUserAddedAsync(UserAddedEvent @event, CancellationToken cancellationToken)
|
|
{
|
|
_statistics.TotalUsersAdded++;
|
|
_statistics.LastUserId = @event.UserId;
|
|
_statistics.LastUserName = @event.Name;
|
|
_statistics.LastUserEmail = @event.Email;
|
|
_statistics.LastUpdated = @event.OccurredAt;
|
|
|
|
_logger.LogInformation(
|
|
"User added: {UserId} ({Name}). Total users: {Total}",
|
|
@event.UserId, @event.Name, _statistics.CurrentUserCount);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task HandleUserRemovedAsync(UserRemovedEvent @event, CancellationToken cancellationToken)
|
|
{
|
|
_statistics.TotalUsersRemoved++;
|
|
_statistics.LastUpdated = @event.OccurredAt;
|
|
|
|
_logger.LogInformation(
|
|
"User removed: {UserId}. Total users: {Total}",
|
|
@event.UserId, _statistics.CurrentUserCount);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|