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
};
}
}
-
The
Company
constructor is declared asinternal
.
Reason: Only the factory should call this constructor.internal
ensures that no other layers can instantiate it (layers are separated by VS projects). -
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 theIIdentityFactory
). -
The
CompanyFactory.CreateExisting(int id, string name)
method will be used by theCompanyRepository
when retrieving items from the database.
Reason: The model would already have identity, so this would need to be supplied to the factory. -
The
CompanyMapper.MapFrom(CompanyTable dataEntity)
will be used by theCompanyRepository
when retrieving data from persistence.
Reason: Here Entity Framework data entities need to be mapped into domain models. TheCompanyFactory
will be used to create the domain model to ensure that business rules are satisfied. -
The
CompanyMapper.MapFrom(Company domainEntity)
will be used by theCompanyRepository
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.