Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Generic Repository with DDD (Domain Model + Persistence Model)?

I have a question. we are using a generic repository and our domain model is also the persistence model but that caused us to tweak our Domain Model a lots to be a aligned with the ORM, for example:- We have to put a default private constructor and some other dirty changes , we are using (Entity Framework in this case) and now we decide to have a persistence model that is different from our rich domain model but we we can't use the Generic Repository in this case. Note:- We user Factories to create our Domain Models but we use AutoMapper to convert from Domain Models to Persistence Model.

like image 315
roro2012 Avatar asked Feb 05 '23 17:02

roro2012


1 Answers

This is a difficult question, because you're trying to reconcile two antagonistic approaches to persistence in DDD that were designed by opposite schools of thought.

The Generic Repository pattern, considered by some as an antipattern, dates back to early DDD adoption when people were in search of tools and techniques to simplify persistence in DDD systems. Most implementations ended up exposing ORM querying specifics (IQueryable in the case of Entity Framework) in the contract of the generic repository because they were a convenient common ground between all sorts of things you could ask a repo.

The more recent Persistence Model approach is a step in the opposite direction - away from the ORM. What you do is introduce an additional layer of indirection, precisely to leave your domain model (which also includes Repository interfaces) untainted by persistence layer specific stuff.

If you're still absolutely positive that Generic Repository is the best tradeoff for you in terms of gain through code reuse (which I would recommend challenging first thing), Greg Young gives us a reasonable middle ground :

So the answer here is to still use a generic repository but to use composition instead of inheritance and not expose it to the domain as a contract.

You could leverage the same approach and take advantage of that seam to throw domain model / persistence model mapping into the mix.

Something along these lines maybe (not tested) :

public class FooRepository
{
    private PersistenceRepository<FooPersistence> _innerRepository;

    public Foo GetFooById(int id)
    {
        return MapToDomain(_innerRepository.GetById(id));
    }

    public void Add(Foo foo)
    {
        _innerRepository.Add(MapToPersistence(foo));
    }

    public IEnumerable<Foo> GetByCity(string city)
    {
        return _innerRepository.Find(f => f.City == city).Select(MapToDomain);
    }

    private Foo MapToDomain(FooPersistence persistenceModel)
    {
        // Mapping stuff here
    }

    private FooPersistence MapToPersistence(Foo foo)
    {
        // Mapping stuff here
    }
}

public class PersistenceRepository<T> where T : PersistenceModel
{
    public T GetById(int id)
    {
        //...
    }

    public void Add(T t)
    {
        //...
    }

    public IQueryable<T> Find(Func<T, bool> predicate)
    {
        //...
    }
}

public abstract class PersistenceModel
{
}

public class FooPersistence : PersistenceModel
{
    public string City { get; set; }
}
like image 177
guillaume31 Avatar answered Feb 08 '23 16:02

guillaume31