Design – DDD: Domain Model Factory Design

architectural-patternsdesigndomain-driven-designdomain-model

I am trying to understand how and where to implement domain model factories. I have included my Company aggregate as a demo of how I have done it.

I have included my design decisions at the end – I would appreciate any comments, suggestions, critique on those points.

The Company domain model:

public class Company : DomainEntity, IAggregateRoot
{
    private string name;
    public string Name
    {
        get
        {
            return name;
        }
        private set
        {
            if (String.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentOutOfRangeException("Company name cannot be an empty value");
            }

            name = value;
        }
    }

    internal Company(int id, string name)
    {
        Name = name;
    }
}

The CompanyFactory domain factory:

This class is used to ensure that business rules and invariants are not violated when creating new instances of domain models. It would reside in the domain layer.

public class CompanyFactory
{
    protected IIdentityFactory<int> IdentityFactory { get; set; }

    public CompanyFactory(IIdentityFactory<int> identityFactory)
    {
        IdentityFactory = identityFactory;
    }

    public Company CreateNew(string name)
    {
        var id = IdentityFactory.GenerateIdentity();

        return new Company(id, name);
    }

    public Company CreateExisting(int id, string name)
    {
        return new Company(id, name);
    }
}

The CompanyMapper entity mapper:

This class is used to map between rich domain models and Entity Framework data entities. It would reside in infrastructure layers.

public class CompanyMapper : IEntityMapper<Company, CompanyTable>
{
    private CompanyFactory factory;

    public CompanyMapper(CompanyFactory companyFactory)
    {
        factory = companyFactory;
    }

    public Company MapFrom(CompanyTable dataEntity)
    {
        return DomainEntityFactory.CreateExisting(dataEntity.Id, dataEntity.Name);
    }

    public CompanyTable MapFrom(Company domainEntity)
    {
        return new CompanyTable()
        {
            Id = domainEntity.Id,
            Name = domainEntity.Name
        };
    }
}
  1. The Company constructor is declared as internal.
    Reason: Only the factory should call this constructor. internal ensures that no other layers can instantiate it (layers are separated by VS projects).

  2. The CompanyFactory.CreateNew(string name) method would be used when creating a new company in the system.
    Reason: Since it would not have been persisted yet, an new unique identity will need to be generated for it (using the IIdentityFactory).

  3. The CompanyFactory.CreateExisting(int id, string name) method will be used by the CompanyRepository when retrieving items from the database.
    Reason: The model would already have identity, so this would need to be supplied to the factory.

  4. The CompanyMapper.MapFrom(CompanyTable dataEntity) will be used by the CompanyRepository when retrieving data from persistence.
    Reason: Here Entity Framework data entities need to be mapped into domain models. The CompanyFactory will be used to create the domain model to ensure that business rules are satisfied.

  5. The CompanyMapper.MapFrom(Company domainEntity) will be used by the CompanyRepository when adding or updating models to persistence.
    Reason: Domain models need to be mapped straight onto data entity properties so that Entity Framework can recognise what changes to make in the database.

Thanks

Best Answer

There is one thing I don't like about your design. And that is fact you are going to have 3 additional classes for each aggregate root (Factory, Mapper, Repository) with semi-duplicate code in form of reading and setting properties everywhere. This will be problematic as you add and remove properties, because forgetting to change a single method might cause an error. And the more complex the domain entity is, the more of this duplicate code is. I don't want to see CompanyFactory.CreateExisting when company has 10 properties and 2 entities in aggregate.

Second thing I might complain about is the IdentityFactory. In DDD, the identity should either be related to domain, in which case you set it to some calculated value. Or it is transparent, in which case you can have DB take care of it. Adding some kind of factory for identity is IMO overenginering.

I also don't like the mapping. While I agree it might not be possible to use EntityFramework directly, you could at least try. EF is getting better with POCO lately and you might be surprised how powerfull it is. But it still isn't NHibernate. If you really want to create separate models, try thinking about using some auto-mapping library.

It is nice you are demoing the design on one simple class, but try also demoing or at least imagining how the design will work with dozens of aggregates that have lots of properties and entities. Also, write a code that will use this design. You might realize that while the design might look good from the inside, it is cumbersome to use from the outside. Also, no one here is going to tell you if the design is appropriate for your team and your domain.