I’m playing around with Domain Driven Development. I’m using a generic repository implementation that is defined as follows: (this is implemented as Repository<T>)
public interface IRepository<T> where T : class, IEventSource, new()
{
T FindByKey(Guid key);
IEnumerable<T> FindByQuery(Predicate<T> filter);
IEnumerable<T> GetAll();
T CreateObject();
}
Let’s say I have an Order class, that cannot be created without a Customer parameter. I’m using CreateObject to set the Key of the entity with a sequential Guid generator.
What is the solution that minimizes development time and coupling?
- Currently I have a parameterless constructor, and am calling some
Initialize(Customer) method. - I could create a ICustomerRepository, but that would mean there is a
lot of extra development time per entity. - I can also modify CreateObject to take
params object[] args, but that is not type safe at compile time. - I could remove CreateObject and use constructors to create the object, but that means I need to have access to the the Guid generation algorithm everywhere I instantiate an object, increasing coupling.
- In a base class for an entity I could set the key in the constructor, reducing coupling, but requiring some static reference to the algorithm.
Update
I’ve implemented the strategy following sll’s answer. The new signature for the repository is now:
public interface IRepository<T> where T : class, IEventSource
{
T FindByKey(Guid key);
IEnumerable<T> FindByQuery(Func<T, bool> predicate);
IEnumerable<T> GetAll();
T CreateObject();
T CreateObject(IConstructorParameters parameters);
}
The parameterless CreateObject creates an instance by attempting to call the parameterless constructor (using IL Emit for performance).
The second CreateObject attempts to create a method that calls the constructor where the properties on the IConstructorParameters match a constructor on the Entity object.
Implementation:
private Dictionary<Type, Func<IConstructorParameters, T>> _constructionMethods
= new Dictionary<Type, Func<IConstructorParameters, T>>();
public T CreateObject(IConstructorParameters args)
{
T newObject;
if (args == null)
{
args = ConstructorParameters.Empty;
}
Type paramType = args.GetType();
Func<IConstructorParameters, T> constructor;
if (!_constructionMethods.TryGetValue(paramType, out constructor))
{
//Emit IL to create a Func<IConstructorParameters,T>
constructor = CreateConstructor(paramType);
_constructionMethods.Add(paramType, constructor);
}
newObject = constructor(args);
newObject.Key = _guidCreator.Generate();
return newObject;
}
If you do not want implement
IOrderRepository– you can abstractCreateObject()method parameters by an interface likeIConstructionParametersand then for each entity implement concrete parameters likeOrderConstructionParameters.This approach also known as Parameter Object design pattern which design rationale – more decoupled system design.