I've came up with a NuGet package, which helps me doing exactly what you said. I'm using the classical DDD approach:
- Domain --> DataModel = AutoMapper
- Domain --> DTO = AutoMapper
- DataModel --> Domain = Factory
- DTO --> Domain = Factory
On first glance, it seems like hell of a overkill to have 3 models, but it allows us to design the domain without having ever to bother with technical concerns, for example having to set read only fields as private setters, because the ORM can't set read only fields etc.
The whole Repository layer code can be found here: https://github.com/DrMueller/MLH.DataAccess Currently, it's using a MongoDB, but the pattern is pretty much the same. An example how to use it, can be found here: https://github.com/DrMueller/MLH.WebApiExtensions
How it works:
Domain Model:
public class Individual : AggregateRoot
{
public Individual(string id, string firstName, string lastName, DateTime birthdate)
: base(id)
{
Guard.ObjectNotNull(() => firstName);
Guard.ObjectNotNull(() => lastName);
FirstName = firstName;
LastName = lastName;
Birthdate = birthdate;
}
public DateTime Birthdate { get; }
public string FirstName { get; }
public string LastName { get; }
}
create a Repository for the Aggregate Root, for example:
public class IndividualRepository : RepositoryBase<Individual, IndividualDataModel>
{
public IndividualRepository(IDataModelRepository<IndividualDataModel> dataModelRepository, IDataModelAdapter<IndividualDataModel, Individual> dataModelAdapter) : base(dataModelRepository, dataModelAdapter)
{
}
}
A datamodel, which represents the "Entity":
public class IndividualDataModel : DataModelBase
{
public DateTime Birthdate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And a DataModelAdapter, which maps in both directions:
public class IndividualDataModelAdapter : DataModelAdapterBase<IndividualDataModel, Individual>
{
private readonly IIndividualFactory _individualFactory;
public IndividualDataModelAdapter(IIndividualFactory individualFactory, IMapper mapper) : base(mapper)
{
_individualFactory = individualFactory;
}
public override Individual Adapt(IndividualDataModel dataModel)
{
return _individualFactory.Create(
dataModel.FirstName,
dataModel.LastName,
dataModel.Birthdate,
dataModel.Id);
}
}
That's pretty much it, the implementation of the Repository has access to the datamodel and queryables, and the Interface works just with the Domain Models.
To answer your question: The infrastructure code can be easily made so generic, that there are minimal delays. All you need to do is to keep the possibility to define the mapping and overrule it for special needs.