I faced the issue of mapping the database entity to the domain entity in DDD.
Here are the details (simplified).
Domain layer with domain aggregate (entity) and the interface for the repository of this aggregate.
Domain aggregate (contains domain logic)
public class DomainBankAccount
{
public DomainBankAccount(int id, DateTimeOffset openedAt)
{
if (openedAt > DateTimeOffset.Now)
throw new ArgumentOutOfRangeException(nameof(openedAt),
"Bank account opened date shouldn't be in the future");
OpenedAt = openedAt;
}
public int Id { get; }
public DateTimeOffset OpenedAt { get; }
public DateTimeOffset? ClosedAt { get; private set; }
public bool IsClosed => ClosedAt.HasValue;
public void Close()
{
if (IsClosed)
throw new InvalidOperationException($"Bank account '{AccountNumber.Value}' is already closed");
ClosedAt = DateTimeOffset.Now;
}
}
Domain repository interface
public interface IRepository
{
Task<DomainBankAccount> GetAsync(int id, CancellationToken cancellation);
}
Now, making the implementation of the IRepository interface I need to:
- Retrieve the database entity from the storage
- Map database entity to domain entity
And here is the place where the problems come. Because all I want - is to map data from database table to domain object. But domain object contains logic and prohibit plain creation with all fields initialization (ClosedAt can't be straightforward initialized via domain constructor).
Repository interface implementation (in the infrastructure\persistence layer)
var dbEntity = new
{
Id = 1,
OpenedAt = DateTimeOffset.Parse("2023-03-17"),
ClosedAt = DateTimeOffset.Parse("2023-03-18")
};
var result = new DomainBankAccount(
dbEntity.Id,
dbEntity.OpenedAt);
var closedAt = result.ClosedAt; // Still null
So, I can't set the ClosedAt property in the domain object directly -> can't map it exactly from the database.
How can I map the database object to the domain object without violating the domain logic and encapsulation and without bringing any logic into the mapper (repository)?
Any ideas would be very appreciated. Thanks!