refactor(application): Simplify SendEmailCommand and IEmailQueue/IEmailService interfaces, add Shared reference

This commit is contained in:
2026-07-23 11:19:54 +02:00
parent 828bb168eb
commit 71e29ac3bb
5 changed files with 18 additions and 51 deletions

View File

@@ -1,4 +1,5 @@
using DigitalData.EmailProfiler.Domain.Entities;
using DigitalData.EmailProfiler.Application.Common.Events;
using DigitalData.EmailProfiler.Application.EmailSending.Commands;
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
@@ -7,14 +8,14 @@ namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
/// </summary>
public interface IEmailQueue
{
Task EnqueueAsync(EmailOutbox email, CancellationToken cancellationToken = default);
Task<EmailOutbox?> DequeueAsync(CancellationToken cancellationToken = default);
Task EnqueueAsync(OutgoingEmailEvent outgoingEmailEvent, CancellationToken cancellationToken = default);
Task<OutgoingEmailEvent?> DequeueAsync(CancellationToken cancellationToken = default);
Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Start event-driven consumer that calls callback when message received
/// Start event-driven consumer that calls callback when mail received
/// </summary>
/// <param name="onMessageReceived">Callback to process received message</param>
/// <param name="onMailReceived">Callback to process received mail</param>
/// <param name="cancellationToken">Cancellation token</param>
Task StartConsumerAsync(Func<EmailOutbox, Task> onMessageReceived, CancellationToken cancellationToken = default);
Task StartConsumerAsync(Func<OutgoingEmailEvent, Task> onMailReceived, CancellationToken cancellationToken = default);
}

View File

@@ -1,5 +1,3 @@
using DigitalData.EmailProfiler.Application.Common.Dtos;
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
/// <summary>

View File

@@ -18,4 +18,8 @@
<PackageReference Include="MimeKit" Version="4.17.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Common\Dtos\" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,6 @@
using AutoMapper;
using DigitalData.EmailProfiler.Application.Common.Events;
using DigitalData.EmailProfiler.Application.Common.Interfaces;
using DigitalData.EmailProfiler.Domain.Entities;
using MediatR;
namespace DigitalData.EmailProfiler.Application.EmailSending.Commands;
@@ -8,13 +8,8 @@ namespace DigitalData.EmailProfiler.Application.EmailSending.Commands;
/// <summary>
/// Command to send an email (enqueue to RabbitMQ)
/// </summary>
public record SendEmailCommand : IRequest<int>
public record SendEmailCommand : IRequest<OutgoingEmailEvent>
{
/// <summary>
/// Email account ID to use for sending
/// </summary>
public required int EmailAccountId { get; init; }
/// <summary>
/// Recipient email address
/// </summary>
@@ -34,37 +29,20 @@ public record SendEmailCommand : IRequest<int>
/// Is HTML email (default: true)
/// </summary>
public bool IsHtml { get; init; } = true;
/// <summary>
/// Optional reference string (e.g., ticket number, order ID)
/// </summary>
public string? ReferenceId { get; init; }
/// <summary>
/// Optional comment
/// </summary>
public string? Comment { get; init; }
}
/// <summary>
/// Handler for SendEmailCommand
/// Creates EmailOutbox entity via AutoMapper and enqueues to RabbitMQ
/// </summary>
public class SendEmailCommandHandler(IEmailQueue EmailQueue, IMapper Mapper) : IRequestHandler<SendEmailCommand, int>
public class SendEmailCommandHandler(IEmailQueue EmailQueue, IMapper Mapper) : IRequestHandler<SendEmailCommand, OutgoingEmailEvent>
{
public async Task<int> Handle(SendEmailCommand request, CancellationToken cancellationToken)
public async Task<OutgoingEmailEvent> Handle(SendEmailCommand request, CancellationToken cancellationToken)
{
// Map command to EmailOutbox entity using AutoMapper
var emailOutbox = Mapper.Map<EmailOutbox>(request);
// Set audit fields
emailOutbox.AddedWhen = DateTime.Now;
emailOutbox.Sent = false;
emailOutbox.RetryCount = 0;
var outgoingEmailEvent = Mapper.Map<OutgoingEmailEvent>(request);
// Enqueue to RabbitMQ
await EmailQueue.EnqueueAsync(emailOutbox, cancellationToken);
return emailOutbox.Id;
await EmailQueue.EnqueueAsync(outgoingEmailEvent, cancellationToken);
return outgoingEmailEvent;
}
}

View File

@@ -10,10 +10,6 @@ public class SendEmailCommandValidator : AbstractValidator<SendEmailCommand>
{
public SendEmailCommandValidator()
{
RuleFor(x => x.EmailAccountId)
.GreaterThan(0)
.WithMessage("EmailAccountId must be greater than 0");
RuleFor(x => x.Recipient)
.NotEmpty()
.WithMessage("Recipient is required")
@@ -31,15 +27,5 @@ public class SendEmailCommandValidator : AbstractValidator<SendEmailCommand>
RuleFor(x => x.Body)
.NotEmpty()
.WithMessage("Body is required");
RuleFor(x => x.ReferenceId)
.MaximumLength(200)
.When(x => !string.IsNullOrEmpty(x.ReferenceId))
.WithMessage("ReferenceId must not exceed 200 characters");
RuleFor(x => x.Comment)
.MaximumLength(500)
.When(x => !string.IsNullOrEmpty(x.Comment))
.WithMessage("Comment must not exceed 500 characters");
}
}