Refactor: Replace EmailAccountDto with EmailAccount

Replaced the `EmailAccountDto` class with the `EmailAccount` class across the codebase to consolidate the `EmailAccount` entity into the domain layer. Updated namespaces, method signatures, property types, and test cases to reflect this change.

Moved `EmailAccount` from `DigitalData.MessagingService.Application.Common.Dto` to `DigitalData.MessagingService.Domain.Entities`. Updated XML documentation and removed redundant project file entries. Adjusted namespaces for related queries, validators, and commands to align with the new structure.

These changes improve separation of concerns and align with domain-driven design principles.
This commit is contained in:
2026-08-12 15:26:15 +02:00
parent 91581649ba
commit b2857c558f
15 changed files with 41 additions and 37 deletions

View File

@@ -1,11 +1,13 @@
namespace DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Application.Common.Dto;
public record EmailContext public record EmailContext
{ {
#if NETFRAMEWORK #if NETFRAMEWORK
public EmailAccountDto Sender { get; set; } = null!; public EmailAccount Sender { get; set; } = null!;
#else #else
public required EmailAccountDto Sender { get; init; } public required EmailAccount Sender { get; init; }
#endif #endif
/// <summary> /// <summary>

View File

@@ -1,6 +1,7 @@
#if NET #if NET
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.Common.Dto.MailSearch; using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Application.Common.Interfaces; namespace DigitalData.MessagingService.Application.Common.Interfaces;
@@ -19,7 +20,7 @@ public interface IImapEmailService
/// </param> /// </param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
Task<IEnumerable<ReceivedEmailDto>> FetchEmailsAsync( Task<IEnumerable<ReceivedEmailDto>> FetchEmailsAsync(
EmailAccountDto account, EmailAccount account,
MailSearchFilter filter, MailSearchFilter filter,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
@@ -27,7 +28,7 @@ public interface IImapEmailService
/// Marks a message as seen (read) on the server. /// Marks a message as seen (read) on the server.
/// </summary> /// </summary>
Task MarkAsSeenAsync( Task MarkAsSeenAsync(
EmailAccountDto account, EmailAccount account,
long uid, long uid,
string folder = "INBOX", string folder = "INBOX",
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);

View File

@@ -1,10 +1,10 @@
#if NET #if NET
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Application.Common.Options; namespace DigitalData.MessagingService.Application.Common.Options;
/// <summary> /// <summary>
/// Wrapper options class that holds a list of <see cref="EmailAccountDto"/> entries /// Wrapper options class that holds a list of <see cref="Dto.EmailAccount"/> entries
/// bound from the <c>EmailAccounts</c> configuration section. /// bound from the <c>EmailAccounts</c> configuration section.
/// </summary> /// </summary>
public class EmailAccountsOptions public class EmailAccountsOptions
@@ -14,7 +14,7 @@ public class EmailAccountsOptions
/// <summary> /// <summary>
/// The list of configured email accounts. /// The list of configured email accounts.
/// </summary> /// </summary>
public required IEnumerable<EmailAccountDto> Accounts { get; init; } = []; public required IEnumerable<EmailAccount> Accounts { get; init; } = [];
/// <summary> /// <summary>
/// How often the IMAP sync worker polls for new emails, in seconds. /// How often the IMAP sync worker polls for new emails, in seconds.

View File

@@ -1,13 +1,13 @@
#if NET #if NET
using DigitalData.MessagingService.Application.Common.Options; using DigitalData.MessagingService.Application.Common.Options;
using DigitalData.MessagingService.Application.Common.Dto;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Application.EmailAccount.Queries; namespace DigitalData.MessagingService.Application.EmailAccounts.Queries;
public record GetSenderQuery : IRequest<EmailAccountDto?> public record GetSenderQuery : IRequest<EmailAccount?>
{ {
public int? Id { get; init; } public int? Id { get; init; }
@@ -19,9 +19,9 @@ public record GetSenderQuery : IRequest<EmailAccountDto?>
/// </summary> /// </summary>
/// <param name="Options"></param> /// <param name="Options"></param>
/// <param name="Logger"></param> /// <param name="Logger"></param>
public class GetSenderQueryHandler(IOptions<EmailAccountsOptions> Options, ILogger<GetSenderQueryHandler> Logger) : IRequestHandler<GetSenderQuery, EmailAccountDto?> public class GetSenderQueryHandler(IOptions<EmailAccountsOptions> Options, ILogger<GetSenderQueryHandler> Logger) : IRequestHandler<GetSenderQuery, EmailAccount?>
{ {
public Task<EmailAccountDto?> Handle(GetSenderQuery request, CancellationToken cancellationToken) public Task<EmailAccount?> Handle(GetSenderQuery request, CancellationToken cancellationToken)
{ {
var accounts = request.Id is not null var accounts = request.Id is not null
? Options.Value.Accounts.Where(a => a.Id == request.Id) ? Options.Value.Accounts.Where(a => a.Id == request.Id)

View File

@@ -1,8 +1,8 @@
#if NET #if NET
using DigitalData.MessagingService.Application.EmailAccount.Queries; using DigitalData.MessagingService.Application.EmailAccounts.Queries;
using FluentValidation; using FluentValidation;
namespace DigitalData.MessagingService.Application.EmailAccount.Validators; namespace DigitalData.MessagingService.Application.EmailAccounts.Validators;
/// <summary> /// <summary>
/// Validator for <see cref="GetSenderQuery"/>. /// Validator for <see cref="GetSenderQuery"/>.

View File

@@ -1,6 +1,6 @@
#if NET #if NET
using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Application.EmailAccount.Queries; using DigitalData.MessagingService.Application.EmailAccounts.Queries;
using DigitalData.MessagingService.Domain.Exceptions; using DigitalData.MessagingService.Domain.Exceptions;
using MediatR; using MediatR;

View File

@@ -2,7 +2,7 @@
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.Common.Dto.MailSearch; using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Application.EmailAccount.Queries; using DigitalData.MessagingService.Application.EmailAccounts.Queries;
using DigitalData.MessagingService.Domain.Exceptions; using DigitalData.MessagingService.Domain.Exceptions;
using MediatR; using MediatR;

View File

@@ -1,11 +1,11 @@
#if NET #if NET
using AutoMapper; using AutoMapper;
using DigitalData.MessagingService.Application.EmailAccount.Queries;
using DigitalData.MessagingService.Domain.Exceptions; using DigitalData.MessagingService.Domain.Exceptions;
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using MediatR; using MediatR;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Application.EmailAccounts.Queries;
namespace DigitalData.MessagingService.Application.EmailSending.Commands; namespace DigitalData.MessagingService.Application.EmailSending.Commands;

View File

@@ -11,8 +11,4 @@
<PackageReference Include="MediatR" Version="12.2.0" /> <PackageReference Include="MediatR" Version="12.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Entities\" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,9 +1,9 @@
namespace DigitalData.MessagingService.Application.Common.Dto; namespace DigitalData.MessagingService.Domain.Entities;
/// <summary> /// <summary>
/// DTO for a single email account configuration. /// DTO for a single email account configuration.
/// </summary> /// </summary>
public class EmailAccountDto public class EmailAccount
{ {
/// <summary> /// <summary>
/// Logical name to identify this account (e.g. "default", "support"). /// Logical name to identify this account (e.g. "default", "support").

View File

@@ -6,6 +6,7 @@ using Limilabs.Mail;
using Limilabs.Mail.Headers; using Limilabs.Mail.Headers;
using DigitalData.MessagingService.Infrastructure.Services.Extensions; using DigitalData.MessagingService.Infrastructure.Services.Extensions;
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Infrastructure.Services; namespace DigitalData.MessagingService.Infrastructure.Services;
@@ -13,7 +14,7 @@ namespace DigitalData.MessagingService.Infrastructure.Services;
/// Email service using Limilabs Mail.dll for SMTP operations (send-only). /// Email service using Limilabs Mail.dll for SMTP operations (send-only).
/// Commercial-grade library with superior Exchange support. /// Commercial-grade library with superior Exchange support.
/// SMTP configuration is injected via IOptions&lt;EmailAccountsOptions&gt; from appsettings.json. /// SMTP configuration is injected via IOptions&lt;EmailAccountsOptions&gt; from appsettings.json.
/// Uses the first account in the list whose <see cref="EmailAccountDto.Name"/> equals <c>"default"</c>, /// Uses the first account in the list whose <see cref="EmailAccount.Name"/> equals <c>"default"</c>,
/// or falls back to the first account if none is named "default". /// or falls back to the first account if none is named "default".
/// </summary> /// </summary>
public class LimilabsEmailService() : IEmailService public class LimilabsEmailService() : IEmailService
@@ -70,7 +71,7 @@ public class LimilabsEmailService() : IEmailService
} }
} }
private static async Task ConnectAndAuthenticateSmtpAsync(Smtp smtp, EmailAccountDto smtpAccount) private static async Task ConnectAndAuthenticateSmtpAsync(Smtp smtp, EmailAccount smtpAccount)
{ {
if (smtpAccount.SmtpUseSsl) if (smtpAccount.SmtpUseSsl)
{ {

View File

@@ -1,6 +1,7 @@
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.Common.Dto.MailSearch; using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Domain.Entities;
using DigitalData.MessagingService.Domain.Exceptions; using DigitalData.MessagingService.Domain.Exceptions;
using DigitalData.MessagingService.Infrastructure.Services.Extensions; using DigitalData.MessagingService.Infrastructure.Services.Extensions;
using Limilabs.Client.IMAP; using Limilabs.Client.IMAP;
@@ -26,7 +27,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
// Public API // Public API
public async Task<IEnumerable<ReceivedEmailDto>> FetchEmailsAsync( public async Task<IEnumerable<ReceivedEmailDto>> FetchEmailsAsync(
EmailAccountDto account, EmailAccount account,
MailSearchFilter filter, MailSearchFilter filter,
CancellationToken cancel = default) CancellationToken cancel = default)
{ {
@@ -188,7 +189,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
} }
public async Task MarkAsSeenAsync( public async Task MarkAsSeenAsync(
EmailAccountDto account, EmailAccount account,
long uid, long uid,
string folder = "INBOX", string folder = "INBOX",
CancellationToken cancel = default) CancellationToken cancel = default)
@@ -213,7 +214,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
} }
} }
private static async Task<Imap> OpenAsync(EmailAccountDto account, string folder, CancellationToken cancel) private static async Task<Imap> OpenAsync(EmailAccount account, string folder, CancellationToken cancel)
{ {
var imap = new Imap(); var imap = new Imap();

View File

@@ -1,5 +1,6 @@
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Client; using DigitalData.MessagingService.Client;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Tests.Integration; namespace DigitalData.MessagingService.Tests.Integration;
@@ -77,7 +78,7 @@ public sealed class EmailSenderTests
{ {
var email = new EmailContext var email = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["hakanttek@gmail.com"], Recipients = ["hakanttek@gmail.com"],
Subject = "EmailSender.Send Integration Test", Subject = "EmailSender.Send Integration Test",
Body = "<p>Sent via EmailSender static client.</p>", Body = "<p>Sent via EmailSender static client.</p>",
@@ -94,7 +95,7 @@ public sealed class EmailSenderTests
{ {
var email = new EmailContext var email = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["hakanttek@gmail.com"], Recipients = ["hakanttek@gmail.com"],
Subject = "Plain Text Test", Subject = "Plain Text Test",
Body = "This is a plain text email.", Body = "This is a plain text email.",

View File

@@ -1,5 +1,6 @@
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Client; using DigitalData.MessagingService.Client;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Tests.Integration; namespace DigitalData.MessagingService.Tests.Integration;
@@ -57,7 +58,7 @@ public sealed class EmailSenderUrlOverloadTests
{ {
var email = new EmailContext var email = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["url-overload-test@example.com"], Recipients = ["url-overload-test@example.com"],
Subject = "URL Overload Integration Test", Subject = "URL Overload Integration Test",
Body = "<p>Sent after URL-based connection.</p>", Body = "<p>Sent after URL-based connection.</p>",

View File

@@ -5,6 +5,7 @@ using DigitalData.MessagingService.RabbitMQ;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using DigitalData.MessagingService.Application.Common.Dto; using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Domain.Entities;
namespace DigitalData.MessagingService.Tests.Integration; namespace DigitalData.MessagingService.Tests.Integration;
@@ -38,7 +39,7 @@ public sealed class SendingEmailPublisherTests : IAsyncDisposable
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Mail = new EmailContext Mail = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["test@example.com"], Recipients = ["test@example.com"],
Subject = "Integration Test - EnqueueAsync", Subject = "Integration Test - EnqueueAsync",
Body = "<p>Hello from integration test.</p>", Body = "<p>Hello from integration test.</p>",
@@ -67,8 +68,8 @@ public sealed class SendingEmailPublisherTests : IAsyncDisposable
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Mail = new EmailContext Mail = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = new List<string> { $"recipient{i}@example.com" }, Recipients = [$"recipient{i}@example.com"],
Subject = $"Integration Test - Batch #{i}", Subject = $"Integration Test - Batch #{i}",
Body = $"Batch message {i}", Body = $"Batch message {i}",
IsHtml = false, IsHtml = false,
@@ -94,7 +95,7 @@ public sealed class SendingEmailPublisherTests : IAsyncDisposable
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Mail = new EmailContext Mail = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["depth-test@example.com"], Recipients = ["depth-test@example.com"],
Subject = "Integration Test - GetQueueDepth", Subject = "Integration Test - GetQueueDepth",
Body = "Queue depth test", Body = "Queue depth test",
@@ -121,7 +122,7 @@ public sealed class SendingEmailPublisherTests : IAsyncDisposable
Id = id, Id = id,
Mail = new EmailContext Mail = new EmailContext
{ {
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" }, Sender = new EmailAccount { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = new List<string> { "serialize@example.com" }, Recipients = new List<string> { "serialize@example.com" },
Subject = "Serialization Test", Subject = "Serialization Test",
Body = "<strong>Bold</strong>", Body = "<strong>Bold</strong>",