1

I've been considering to inherit my Persistence Models (PM) from my pristine Domain Model (DM) classes in order to add some properties that are important for persistence logic. This way I won't have to deal with a lot of mapping code.

For the sake of a simple example case, here's a DM representing a Credit that can be used by Users or Organizations to place Orders.

namespace Foo.Domain
{
    // Domain Model
    public class Credit : IDomainEntity, IAggregateRoot
    {
        public Credit(int? userId, int? organizationId, DateTime expiryDate)
        {
            if(userId == null && organizationId == null)
                throw new InvalidOperationException("MUST assign credit to user OR organization.");
            if(userId != null && organizationId != null)
                throw new InvalidOperationException("CANNOT assign credit to user AND organization.");

            UserId = userId;
            OrganizationId = organizationId;
            ExpiryDate = expiryDate;
        }

        public virtual int Id { get; private set; }
        public virtual DateTime ExpiryDate { get; private set; }

        // external IAggregateRoot objects are linked by reference
        public virtual int? UserId { get; private set; }
        public virtual int? OrganizationId { get; private set; }
        public virtual int? OrderId { get; private set; }

        public void AttachToOrder(int orderId)
        {
            if (ExpiryDate >= DateTime.UtcNow)
                throw new InvalidOperationException("Credit is expired.");
            if (OrderId != null)
                throw new InvalidOperationException("Credit is unavailable.");

            OrderId = orderId;
        }

        public void DetachFromOrder()
        {
            if (OrderId == null)
                throw new InvalidOperationException("Credit is available.");

            OrderId = null;
        }
    }
}

Let's say that I want to persist this model with a foreign key constraints. Entity Framework Fluent API requires a navigation property to do that. So I can't directly persist the Domain Model and benefit from the constraint checking of a foreign key in the relational database.

My idea is to inherit a PM to add the required navigation properties. Since EF also requires a parameterless constructor, I would unfortunately be forced to add that to the DM, although it luckily won't have an effect on its invariants.

namespace Foo.Domain
{
    // Adjusted Domain Model
    public class Credit : IDomainEntity, IAggregateRoot
    {
        protected Credit() { }

        public Credit(int? userId, int? organizationId, DateTime expiryDate)
            : this()

        // remainder of the class unchanged
    }
}

namespace Foo.Infrastructure
{
    // Persistence Model
    public class Credit : Foo.Domain.Credit
    {
        protected Credit()
            : base()
        {
        }

        public Credit(int? userId, int? organizationId, DateTime expiryDate)
            : base(userId, organizationId, expiryDate)
        {
        }

        public virtual User User { get; private set; }
        public virtual Organization Organization { get; private set; }
        public virtual Order Order { get; private set; }
    }
}

Now I assume that I can then use it like this in the Fluent API:

modelBuilder.Entity<Credit>()
    .HasOptional<Order>(c => c.Order) // this is why I need the navigation property
    .WithMany()
    .HasForeignKey(c => c.OrderId);

..and define my DbContext like this:

namespace Foo.Infrastructure
{
    public interface IFooDbContext
    {
        IDbSet<Credit> Credits { get; }

        Task<int> SaveChangesAsync();
    }
}

What are your thoughts on this approach?

Community
  • 1
  • 1
Korijn
  • 1,383
  • 9
  • 27
  • It is possible to configure ForeignKey w/o a property on the model class, I just can't find that article anymore – Tseng Mar 17 '15 at 11:12
  • Well, I've searched high and low and couldn't find information supporting that statement, hence my question. I'd love to see that! – Korijn Mar 17 '15 at 11:20

1 Answers1

0

For me, main disadvantage is converting from Domain Entity to Persistence Entity. When you get from repository - it's OK, you can just simple cast from Foo.Infrastructure.Credit to Foo.Domain.Credit without problems (becase of inheritance). But when you need to save new Foo.Domain.Credit, you have to create new object Foo.Infrastructure.Credit and map properties from first to second. So, you cant just call new Foo.Domain.Credit, you need to use a factory, and this factory must know about Foo.Infrastructure.Credit, and this looks not good. Unfortunately Entity Framework doesn't let us to design our domain entites as "pure", really persistance ignorant, so we must deal with it.

Backs
  • 24,430
  • 5
  • 58
  • 85