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

1

6

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.

Inurbane answered 14/11, 2016 at 8:5 Comment(9)
"we can't use the Generic Repository in this case" - why? And why do you need it in the first place?Hagiology
Hi, I need it to simplify the development processInurbane
I don't need to repeat the same code in every repositoryInurbane
What code do you not want to repeat, how do you achieve that and why is it not compatible with a persistence model? Also have a look at blog.sapiensworks.com/post/2012/03/05/…Hagiology
For example, Generic quires that accept predicatesInurbane
I mean, when i use Generic Repository, most of the code is being at the Generic repository like Add/Update/Delete/Read and a lot of common quiresInurbane
In the Generic Repository, I depend on TEntity but in case of Having Persistence model and Domain Model, i can't depend on TEntity as each Aggregate will have different factories or constcutorsInurbane
Entity Framework IS a repository, why build another one on top of it?Prototherian
What's the question?Ulda
H
9

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; }
}
Hagiology answered 15/11, 2016 at 13:7 Comment(9)
Thanks for Great SolutionInurbane
I have another issue. How to use Specification Pattern in this new wayInurbane
note, The specifications is a domain model behavior and a not persistence oneInurbane
I guess you would need a Func<Foo, bool> to Func<FooPersistence, bool> mapper.Hagiology
Could you clarify with an example, Please?Inurbane
What's your intent? A Find(FooSpecification spec) method that calls Find(FooPersistenceSpecification(spec) in the inner repo?Hagiology
Hi, This is one of my specification Specification<Country> specification = new DirectSpecification<Country>(c => c.Name.ToLower().Contains(text.ToLower()));Inurbane
and the old Repos accept ISpecification and call its SatisfiedBy methodInurbane
If you want to keep things that way, you need something that, essentially, is able to map from a Specification<Foo> to a Specification<FooPersistence>. Might be feasible by dissecting the predicate or having domain model and persistence model inherit the same class. At which point it's getting pretty convoluted. Better to avoid generic Specifications in domain repositories and leave it to inner persistence repos IMO.Hagiology

© 2022 - 2024 — McMap. All rights reserved.