Refactor domain model and exception handling
Significantly restructured the `DigitalData.MessagingService.Domain` project by removing unused domain-specific classes, enums, and value objects. Key changes include: - Updated `ExceptionHandlingMiddleware` to handle `FluentValidation.ValidationException` with formatted validation errors mapped to `HttpStatusCode.BadRequest`. - Removed foundational domain classes such as `BaseEntity`, `ValueObject`, and `IAggregateRoot`. - Deleted enums (`AttachmentStatus`, `AuthenticationType`, `EmailStatus`, `ErrorCode`, `ProcessType`) and domain exceptions (`DomainException`, `AttachmentProcessingException`, `DmsNotAvailableException`, `InvalidPdfException`, `ValidationException`). - Removed value objects (`EmailAddress`, `MessageId`) and the `MessageIdGenerator` service. - Cleaned up the project structure by removing the `Events` folder reference. These changes simplify the domain model, reduce unused code, and align the project with updated architectural goals.
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Common;
|
||||
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? CreatedBy { get; set; }
|
||||
public DateTime? ModifiedDate { get; set; }
|
||||
public string? ModifiedBy { get; set; }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Domain-wide constants
|
||||
/// </summary>
|
||||
public static class DomainConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Email processing constants
|
||||
/// </summary>
|
||||
public static class Email
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of retry attempts for failed email sending
|
||||
/// After this limit, email will be moved to Dead Letter Queue (DLQ)
|
||||
/// </summary>
|
||||
public const int MaxRetryCount = 3;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for aggregate roots in DDD
|
||||
/// </summary>
|
||||
public interface IAggregateRoot
|
||||
{
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for value objects that implement equality by value
|
||||
/// </summary>
|
||||
public abstract class ValueObject
|
||||
{
|
||||
protected abstract IEnumerable<object> GetEqualityComponents();
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var other = (ValueObject)obj;
|
||||
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetEqualityComponents()
|
||||
.Select(x => x?.GetHashCode() ?? 0)
|
||||
.Aggregate((x, y) => x ^ y);
|
||||
}
|
||||
|
||||
public static bool operator ==(ValueObject? left, ValueObject? right)
|
||||
{
|
||||
if (left is null && right is null)
|
||||
return true;
|
||||
|
||||
if (left is null || right is null)
|
||||
return false;
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ValueObject? left, ValueObject? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,4 @@
|
||||
<PackageReference Include="MediatR" Version="12.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Events\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
public enum AttachmentStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Valid = 2,
|
||||
Corrupt = 3,
|
||||
Skipped = 4
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
public enum AuthenticationType
|
||||
{
|
||||
UsernamePassword = 1,
|
||||
OAuth2 = 2
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
public enum EmailStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Processing = 2,
|
||||
Processed = 3,
|
||||
Failed = 4,
|
||||
PartiallyProcessed = 5,
|
||||
Rejected = 6
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
public enum ErrorCode
|
||||
{
|
||||
None = 0,
|
||||
NoAttachments = 10001,
|
||||
SenderValidationFailed = 10002,
|
||||
EmbeddedFileAttachmentCorrupt = 10003,
|
||||
NormalFileAttachmentCorrupt = 10004,
|
||||
PdfStructureInvalid = 10005,
|
||||
ImapConnectionFailed = 10006,
|
||||
WindreamImportFailed = 10007,
|
||||
DiskSpaceInsufficient = 10008,
|
||||
DuplicateMessageId = 10009,
|
||||
AttachmentExtractionFailed = 10010
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
public enum ProcessType
|
||||
{
|
||||
ProcessManager = 1, // Easy Approval workflow
|
||||
AttachmentSniffer = 2, // General attachment extraction
|
||||
ZugFeRDParser = 3 // Electronic invoice processing
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
public class AttachmentProcessingException : DomainException
|
||||
{
|
||||
public AttachmentProcessingException(ErrorCode errorCode, string message) : base(message, errorCode)
|
||||
{
|
||||
}
|
||||
|
||||
public AttachmentProcessingException(ErrorCode errorCode, string message, Exception innerException)
|
||||
: base(message, errorCode)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when DMS (windream) is not available or not configured properly.
|
||||
/// </summary>
|
||||
public class DmsNotAvailableException : Exception
|
||||
{
|
||||
public DmsNotAvailableException()
|
||||
: base("DMS service is not available. windream COM components may not be registered.")
|
||||
{
|
||||
}
|
||||
|
||||
public DmsNotAvailableException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DmsNotAvailableException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
public class DomainException : Exception
|
||||
{
|
||||
public ErrorCode? ErrorCode { get; }
|
||||
|
||||
public DomainException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DomainException(string message, ErrorCode errorCode) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public DomainException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when a PDF file is invalid or corrupted.
|
||||
/// </summary>
|
||||
public class InvalidPdfException : Exception
|
||||
{
|
||||
public InvalidPdfException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public InvalidPdfException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using DigitalData.MessagingService.Domain.Enums;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
public class ValidationException : DomainException
|
||||
{
|
||||
public ValidationException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ValidationException(string message, ErrorCode errorCode) : base(message, errorCode)
|
||||
{
|
||||
}
|
||||
|
||||
public ValidationException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using DigitalData.MessagingService.Domain.ValueObjects;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.Services;
|
||||
|
||||
public interface IMessageIdGenerator
|
||||
{
|
||||
MessageId Generate(string originalMessageId, string sender, DateTime date, string subject);
|
||||
List<string> GenerateFallbackHashes(MessageId messageId);
|
||||
}
|
||||
|
||||
public class MessageIdGenerator : IMessageIdGenerator
|
||||
{
|
||||
public MessageId Generate(string originalMessageId, string sender, DateTime date, string subject)
|
||||
{
|
||||
return MessageId.Create(originalMessageId, sender, date, subject);
|
||||
}
|
||||
|
||||
public List<string> GenerateFallbackHashes(MessageId messageId)
|
||||
{
|
||||
// Legacy behavior: 10 variations for duplicate detection
|
||||
var hashes = new List<string> { messageId.Hash };
|
||||
|
||||
// Add variations (this is simplified - legacy had 10 variations)
|
||||
// TODO: Implement exact legacy fallback algorithm if needed
|
||||
|
||||
return hashes;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DigitalData.MessagingService.Domain.Common;
|
||||
using DigitalData.MessagingService.Domain.Exceptions;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.ValueObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Value object representing an email address with validation
|
||||
/// </summary>
|
||||
public class EmailAddress : ValueObject
|
||||
{
|
||||
public string Value { get; private set; }
|
||||
public string Domain { get; private set; }
|
||||
public string LocalPart { get; private set; }
|
||||
|
||||
private EmailAddress(string value)
|
||||
{
|
||||
Value = value;
|
||||
var parts = value.Split('@');
|
||||
LocalPart = parts[0];
|
||||
Domain = parts[1];
|
||||
}
|
||||
|
||||
public static EmailAddress Create(string email)
|
||||
{
|
||||
if (!IsValid(email))
|
||||
throw new DomainException($"Invalid email address: {email}");
|
||||
|
||||
return new EmailAddress(email.ToLowerInvariant());
|
||||
}
|
||||
|
||||
private static bool IsValid(string email)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(email) &&
|
||||
email.Contains('@') &&
|
||||
new EmailAddressAttribute().IsValid(email);
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
yield return Value;
|
||||
}
|
||||
|
||||
public override string ToString() => Value;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DigitalData.MessagingService.Domain.Common;
|
||||
|
||||
namespace DigitalData.MessagingService.Domain.ValueObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Value object representing a unique message identifier with hash generation
|
||||
/// Uses the same algorithm as legacy system for compatibility
|
||||
/// </summary>
|
||||
public class MessageId : ValueObject
|
||||
{
|
||||
public string Value { get; private set; }
|
||||
public string Hash { get; private set; }
|
||||
|
||||
private MessageId(string value)
|
||||
{
|
||||
Value = value;
|
||||
Hash = GenerateHash(value);
|
||||
}
|
||||
|
||||
public static MessageId Create(string originalMessageId, string sender, DateTime date, string subject)
|
||||
{
|
||||
var combined = $"{originalMessageId}|{sender}|{date:yyyyMMddHHmmss}|{subject}";
|
||||
return new MessageId(combined);
|
||||
}
|
||||
|
||||
private static string GenerateHash(string input)
|
||||
{
|
||||
// Same algorithm as legacy: SHA256 hash
|
||||
using var sha256 = SHA256.Create();
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
var hash = sha256.ComputeHash(bytes);
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
yield return Value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user