using System; using Svrnty.CQRS.Events.Abstractions.EventStore; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Svrnty.CQRS.Events.Abstractions; using Svrnty.CQRS.Events.Abstractions.Models; namespace Svrnty.CQRS.Events.Storage; /// /// In-memory implementation of IEventStore for testing and development. /// Thread-safe but data is lost on application restart. /// public sealed class InMemoryEventStore : IEventStore { private readonly ConcurrentDictionary _events = new(); private long _sequenceCounter = 0; public Task AppendAsync(ICorrelatedEvent @event, CancellationToken cancellationToken = default) { var sequence = Interlocked.Increment(ref _sequenceCounter); var storedEvent = new StoredEvent { EventId = @event.EventId, CorrelationId = @event.CorrelationId, EventType = @event.GetType().Name, Sequence = sequence, Event = @event, OccurredAt = @event.OccurredAt, StoredAt = DateTimeOffset.UtcNow }; _events.TryAdd(@event.EventId, storedEvent); return Task.FromResult(sequence); } public async Task> AppendBatchAsync(IEnumerable events, CancellationToken cancellationToken = default) { var result = new Dictionary(); foreach (var @event in events) { var sequence = await AppendAsync(@event, cancellationToken); result[@event.EventId] = sequence; } return result; } public Task> GetEventsAsync( string correlationId, long afterSequence = 0, HashSet? eventTypes = null, CancellationToken cancellationToken = default) { var query = _events.Values .Where(e => e.CorrelationId == correlationId) .Where(e => e.Sequence > afterSequence); if (eventTypes != null && eventTypes.Count > 0) { query = query.Where(e => eventTypes.Contains(e.EventType)); } var result = query .OrderBy(e => e.Sequence) .ToList(); return Task.FromResult(result); } public Task GetEventByIdAsync(string eventId, CancellationToken cancellationToken = default) { _events.TryGetValue(eventId, out var storedEvent); return Task.FromResult(storedEvent); } public Task DeleteOldEventsAsync(DateTimeOffset olderThan, CancellationToken cancellationToken = default) { var toDelete = _events.Values .Where(e => e.StoredAt < olderThan) .Select(e => e.EventId) .ToList(); int deletedCount = 0; foreach (var eventId in toDelete) { if (_events.TryRemove(eventId, out _)) { deletedCount++; } } return Task.FromResult(deletedCount); } }