refactor(infrastructure): Refactor RabbitMqEmailQueue and remove InMemoryEmailQueue, update DbContext

This commit is contained in:
2026-07-23 11:19:59 +02:00
parent 71e29ac3bb
commit 3be6e28477
3 changed files with 10 additions and 79 deletions

View File

@@ -1,4 +1,3 @@
using DigitalData.EmailProfiler.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DigitalData.EmailProfiler.Infrastructure.Persistence; namespace DigitalData.EmailProfiler.Infrastructure.Persistence;
@@ -9,13 +8,4 @@ namespace DigitalData.EmailProfiler.Infrastructure.Persistence;
/// </summary> /// </summary>
public class EmailProfilerDbContext(DbContextOptions<EmailProfilerDbContext> options) : DbContext(options) public class EmailProfilerDbContext(DbContextOptions<EmailProfilerDbContext> options) : DbContext(options)
{ {
// DbSets for all entities
public DbSet<EmailAccount> EmailAccounts { get; set; }
public DbSet<EmailProfile> EmailProfiles { get; set; }
public DbSet<EmailHistory> EmailHistories { get; set; }
public DbSet<EmailAttachment> EmailAttachments { get; set; }
public DbSet<EmailProcess> EmailProcesses { get; set; }
public DbSet<ProcessStep> ProcessSteps { get; set; }
public DbSet<IndexingStep> IndexingSteps { get; set; }
public DbSet<EmailOutbox> EmailOutbox { get; set; }
} }

View File

@@ -1,59 +0,0 @@
using System.Threading.Channels;
using DigitalData.EmailProfiler.Application.Common.Interfaces;
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Infrastructure.Queue;
/// <summary>
/// In-memory email queue implementation using System.Threading.Channels.
/// Thread-safe, high-performance queue for outgoing emails.
///
/// NOTE: This class is OBSOLETE. Use RabbitMqEmailQueue for production.
/// InMemoryEmailQueue does not persist messages and will lose data on application restart.
/// </summary>
[Obsolete("InMemoryEmailQueue is obsolete. Use RabbitMqEmailQueue for production deployment.")]
public class InMemoryEmailQueue : IEmailQueue
{
private readonly Channel<EmailOutbox> _channel;
public InMemoryEmailQueue()
{
var options = new BoundedChannelOptions(1000)
{
FullMode = BoundedChannelFullMode.Wait
};
_channel = Channel.CreateBounded<EmailOutbox>(options);
}
public async Task EnqueueAsync(EmailOutbox email, CancellationToken cancellationToken = default)
{
await _channel.Writer.WriteAsync(email, cancellationToken);
}
public async Task<EmailOutbox?> DequeueAsync(CancellationToken cancellationToken = default)
{
if (await _channel.Reader.WaitToReadAsync(cancellationToken))
{
if (_channel.Reader.TryRead(out var email))
{
return email;
}
}
return null;
}
public Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(_channel.Reader.Count);
}
/// <summary>
/// NOT IMPLEMENTED - InMemoryEmailQueue does not support event-driven consumers
/// </summary>
public Task StartConsumerAsync(Func<EmailOutbox, Task> onMessageReceived, CancellationToken cancellationToken = default)
{
throw new NotSupportedException("InMemoryEmailQueue does not support StartConsumerAsync. Use RabbitMqEmailQueue for event-driven consumers.");
}
}

View File

@@ -1,13 +1,13 @@
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using DigitalData.EmailProfiler.Application.Common.Interfaces; using DigitalData.EmailProfiler.Application.Common.Interfaces;
using DigitalData.EmailProfiler.Domain.Common; using DigitalData.EmailProfiler.Application.EmailSending.Commands;
using DigitalData.EmailProfiler.Domain.Entities;
using DigitalData.EmailProfiler.Infrastructure.Messaging; using DigitalData.EmailProfiler.Infrastructure.Messaging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using DigitalData.EmailProfiler.Application.Common.Events;
namespace DigitalData.EmailProfiler.Infrastructure.Queue; namespace DigitalData.EmailProfiler.Infrastructure.Queue;
@@ -126,11 +126,11 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
await _initializationTask.Value; await _initializationTask.Value;
} }
public async Task EnqueueAsync(EmailOutbox email, CancellationToken cancellationToken = default) public async Task EnqueueAsync(OutgoingEmailEvent outgoingEmailEvent, CancellationToken cancellationToken = default)
{ {
await EnsureInitializedAsync(); // Initialize on first call await EnsureInitializedAsync(); // Initialize on first call
var json = JsonSerializer.Serialize(email); var json = JsonSerializer.Serialize(outgoingEmailEvent);
var body = Encoding.UTF8.GetBytes(json); var body = Encoding.UTF8.GetBytes(json);
var properties = new BasicProperties var properties = new BasicProperties
@@ -149,7 +149,7 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
} }
public async Task<EmailOutbox?> DequeueAsync(CancellationToken cancellationToken = default) public async Task<OutgoingEmailEvent?> DequeueAsync(CancellationToken cancellationToken = default)
{ {
await EnsureInitializedAsync(); // Initialize on first call await EnsureInitializedAsync(); // Initialize on first call
@@ -161,7 +161,7 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
try try
{ {
var json = Encoding.UTF8.GetString(result.Body.ToArray()); var json = Encoding.UTF8.GetString(result.Body.ToArray());
var email = JsonSerializer.Deserialize<EmailOutbox>(json); var email = JsonSerializer.Deserialize<OutgoingEmailEvent>(json);
// Acknowledge message after successful deserialization // Acknowledge message after successful deserialization
await _channel.BasicAckAsync(result.DeliveryTag, false, cancellationToken); await _channel.BasicAckAsync(result.DeliveryTag, false, cancellationToken);
@@ -188,7 +188,7 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
/// Start event-driven consumer that processes messages as they arrive /// Start event-driven consumer that processes messages as they arrive
/// </summary> /// </summary>
public async Task StartConsumerAsync( public async Task StartConsumerAsync(
Func<EmailOutbox, Task> onMessageReceived, Func<OutgoingEmailEvent, Task> onMailReceived,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
await EnsureInitializedAsync(); // Initialize on first call await EnsureInitializedAsync(); // Initialize on first call
@@ -200,14 +200,14 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
try try
{ {
var json = Encoding.UTF8.GetString(args.Body.ToArray()); var json = Encoding.UTF8.GetString(args.Body.ToArray());
var email = JsonSerializer.Deserialize<EmailOutbox>(json); var email = JsonSerializer.Deserialize<OutgoingEmailEvent>(json);
if (email != null) if (email != null)
{ {
_logger.LogDebug("Received email message: To={To}, Subject={Subject}", email.Recipient, email.Subject); _logger.LogDebug("Received email message: To={To}, Subject={Subject}", email.Recipient, email.Subject);
// Process message via callback // Process message via callback
await onMessageReceived(email); await onMailReceived(email);
// Acknowledge message after successful processing // Acknowledge message after successful processing
await _channel.BasicAckAsync(args.DeliveryTag, false, cancellationToken); await _channel.BasicAckAsync(args.DeliveryTag, false, cancellationToken);
@@ -225,7 +225,7 @@ public class RabbitMqEmailQueue : IEmailQueue, IDisposable
// TODO: Error Reporting Strategy // TODO: Error Reporting Strategy
// Option 1: Separate RabbitMQ Queue (emailprofiler.errors) // Option 1: Separate RabbitMQ Queue (emailprofiler.errors)
// - Create EmailErrorReport entity { EmailOutboxId, Exception, StackTrace, Timestamp, RetryAttempt } // - Create EmailErrorReport entity { OutgoingEmailEventId, Exception, StackTrace, Timestamp, RetryAttempt }
// - Publish to error queue: await _errorQueue.EnqueueAsync(errorReport) // - Publish to error queue: await _errorQueue.EnqueueAsync(errorReport)
// - Separate worker processes error queue → Log to DB/File/External monitoring // - Separate worker processes error queue → Log to DB/File/External monitoring
// //