8

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

2

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.

  • Thanks for your answer. We have ruled out EF POCO as domain models as it introduces constraints on how we design our models. We could use an automapping library, but that would only be applicable when mapping to EF data entities and not to domain models. Reason being: Mapping to a domain model should use a factory, shouldn't it? Otherwise you risk the chance of invalid domain models floating around your system. I agree that there is a lot of "piping code". Adding new aggregates, or even adding properties to existing entities, requires quite a bit of scaffolding code. – Dave New Feb 3 '14 at 13:13
  • 1
    +1. I agree especially about the "Mapper" - to my understanding, a good ORM should make the distinction between "domain classes" and "entity classes" unneccessary. Do you have a suggestion for an alternative to the CompanyFramework.CreateExisting construct? One thing I don't see as a weakness is the IIdentityFactory. If the IDs are generated from a database, you should have a mechanics to mock this out, to allow unit testing with a database. – Doc Brown Feb 3 '14 at 13:15
  • DocBrown is spot on regarding IIdentityFactory. In DDD, entities need to have identity from the moment of creation. Unfortunately we do not have the luxury of being able to use Guid types. We have to generate int Ids from the database before persistence occurs. Another reason for a factory. – Dave New Feb 3 '14 at 13:19
  • 1
    @DocBrown The alternative to CompanyFramework.CreateExisting is a good ORM. The thing with IDs is that unless the ID is domain concern, in which case you generate it in some way, there should be no IDs. IDs are for DB to keep information about relations, but in DDD, that should be handled by object references. In which case, there is no need to generate it before it is saved in DB. – Euphoric Feb 3 '14 at 13:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.