How to use the repository pattern correctly?

18,405

Solution 1

One thing that i did wrong when played around with repository pattern - just like you, i thought that table relates to repository 1:1. When we apply some rules from Domain Driven Design - grouping repositories problem often disappears.

Repository should be per Aggregate root and not table. It means - if entity shouldn't live alone (i.e. - if you have a Registrant that participates in particular Registration) - it's just an entity, it doesn't need a repository, it should be updated/created/retrieved through repository of aggregate root it belongs.

Of course - in many cases, this technique of reducing count of repositories (actually - it's more a technique to structure your domain model) can't be applied because every entity is supposed to be an aggregate root (that highly depends on your domain, I can provide blind guesses only). In your example - License seems to be an aggregate root because you need to be able to check them with no context of Registration entity.

But that does not restrict us to cascade repositories (Registration repository is allowed to reference License repository if needed). That does not restrict us to reference License repository (preferable - through IoC) directly from Registration object.

Just try not to drive your design through complications provided by technologies or misunderstanding something. Grouping repositories in ServiceX just because you don't want to construct 2 repositories ain't good idea.

Much better would be to give it a proper name - RegistrationService i.e.

But services should be avoided in general - they often are cause that leads to anemic domain model.

EDIT:
Do start to use IoC. It truly eases the pain of injecting dependencies.
Instead of writing:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

you will be able to write:

var registrationService = IoC.Resolve<IRegistrationService>();

P.s. Would be better to use so called common service locator but that's just an example.

Solution 2

One thing I've started doing to address this is to actually develop services that wrap N repositories. Hopefully your DI or IoC frameworks can help to make that easier.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Does that make sense? Also, I understand that speaking of services in this manor may or may not actually comply with DDD principles, I just do it because it seems to work.

Solution 3

What I am doing is I have a abstract base class defined as follows:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

You can then derive you repository from the abstract base class and extend your single repository as long at the generic arguments differ for example;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

etc....

I need the separate repositories because we do have restrictions on some of our repositories and this gives us maximum flexibility. Hope this helps.

Solution 4

I have this as my repository class and yeah I extend in the table / area repository but still I sometimes have to break DRY.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

EDIT

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

I also have a datacontext which was created using Linq2SQL.dbml class.

So now I have a standard repository implementing standard calls like All and Find and in my AdminRepository I have specific calls.

Doesn't answer the question of DRY though I don't think.

Solution 5

Here is an example of a generic Repository implementation using FluentNHibernate. It is capable of persisting any class that you have written a mapper for. It is even capable of generating your database based off of the mapper classes.

Share:
18,405
chobo2
Author by

chobo2

Updated on June 22, 2022

Comments

  • chobo2
    chobo2 almost 2 years

    I am wondering how should I be grouping my repositories? Like from the examples I seen on the asp.net mvc and in my books they basically use one repository per database table. But that seems like a lot of repositories leading you to have to call up many repositories later on for mocking and stuff.

    So I am guessing I should group them. However I am not sure how to group them.

    Right now I made a registration Repository to handle all my registration stuff. However there are like 4 tables I need to update and before I had 3 repositories to do this.

    For example one of the tables is a license table. When they register I look at their key and check it to see if exists in the database. Now what happens if I need to check this license key or something else from that table at some other spot other then registration?

    One spot could be login(check if the key is not expired).

    So what would I do in this situation? Rewrite the code again(break DRY)? Try to merege these 2 repositories together and hope that none of the methods are needed in some other point of time(like maybe I might have a method that checks if userName is used - maybe I will need that somewhere else).

    Also if I merge them together I would either need 2 service layers going to the same repository since I think having all the logic for 2 different parts of a site would be long and I would have to have names like ValidateLogin(), ValdiateRegistrationForm(),ValdiateLoginRetrievePassword() and etc.

    Or call the Repository anyways and just have a weird sounding name around?

    It just seems hard to make a repository that has a general enough name so you can use it for many spots of your application and still make sense and I don't think calling another repository in a repository would be a good practice?

  • chobo2
    chobo2 over 14 years
    Nopethat does not make much sense. I don't use DI or IoC frameworks at this current time because I got enough on my plate as it is.
  • chobo2
    chobo2 over 14 years
    So your trying to make a generic repository to deal with this all?
  • chobo2
    chobo2 over 14 years
    What " Repository" for? and like CreateInstance? Not sure what everything you have is doing.
  • griegs
    griegs over 14 years
    It's generic as anything. Basically you need to have a specific repository for your (area). Check out the edit above for my AdminRepository.
  • neouser99
    neouser99 over 14 years
    If you can instantiate your repos w/ just a new(), you could try this... public ServiceImpl() : this(new Repo1, new Repo2...) {} as an additional constructor in the service.
  • neouser99
    neouser99 over 14 years
    I would give a +1 here, but he has indicated that DI or IoC containers are not quite an option (granted those aren't the only benefits of Sharp Arch). I'm guessing there is some to a lot of existing code the he is working around.
  • chobo2
    chobo2 over 14 years
    Dependency injection? I do that already but still not sure what your code is dong and what it solves.
  • chobo2
    chobo2 over 14 years
    What is considered a entity? Is that the entire database? or is that a database table? DI or IoC containers are not a option at this time since I just don't want to learn that ontop of the other 10 things I am learning at the same time. they are something I will look into my next revision of my site or my next project. Even though I am not sure if it will be this one a quick look at the site and it seems to want you to use nhirbrate and I am using to linq to sql at this current time.
  • chobo2
    chobo2 over 14 years
    So what actually goes into say the Update method. Like you have like this V update and it pass in a T entitytoUpdate but there is no code actually updating it or is there?
  • neouser99
    neouser99 over 14 years
    I'm specifically going after the merging of repositories. Which code does not make sense? If you are using DI, then the code in the answer will work in the sense that your DI framework will inject those IRepo services, in the comment code it's basically just a small work around to do DI (basically your no parameter constructor 'injects' those dependencies into your ServiceImpl).
  • Michael Mann
    Michael Mann over 14 years
    Yes.. There will be code in the update method because you will write a class that descends from the generic repository. The implementation code can be Linq2SQL or ADO.NET or whatever you have chosen as your data access implementation technology
  • chobo2
    chobo2 over 14 years
    I use DI already so I can unit test better. My problem is that if you make your service layers and repos with to detailed of names. Then if you ever need to use them anywhere else it will look weird calling like the RegistrationService Layer that Calls the RegRepo in some other class say like the ProfileClass. So I am not seeing from you example what your fully doing. Like If you start to have too many Repos in the same service layer your going to have so much different bussiness logic and validation logic. Since in the service layer you usually put in validation logic. So many I need more...
  • chobo2
    chobo2 over 14 years
    Ok I think I sort of get what your saying now. In another service layer you would bring in the other Repo and then make a wrapper method around that. So you if your working in the ProfileService layer and need userName you don't have to call the RegService you jsut call the RegRepo and have a method in your ProfileService like GetUserName{//call RegRepo.GetUser()}. So ya that can work but still comes done to the name. Still think it would be weird if your creating a registration object in say a ProfileService layer. To me I think the hardest thing about the Repoistory Pattern is the naming...
  • Arnis Lapsa
    Arnis Lapsa over 14 years
    -1 => creating services just to group repositories ain't right at all.
  • Arnis Lapsa
    Arnis Lapsa over 13 years
    Hahaha... I'm giving advice to use service locator. Always joy to see how dumb I've been in past.
  • ngm
    ngm about 13 years
    @Arnis It sounds as if your opinions have changed -- I'm interested how would you answer this question differently now?
  • Arnis Lapsa
    Arnis Lapsa about 13 years
    @ngm a lot have changed since I answered this. I still agree that aggregate root should draw transactional boundaries (be saved as a whole), but I'm much less optimistic about abstracting persistence using repository pattern. Lately - I'm just using ORM directly, because things like eager/lazy loading management are too awkward. It's much more beneficial to focus on developing rich domain model instead of focusing on abstracting persistence.
  • Karl Nicoll
    Karl Nicoll about 13 years
    @Arnis - Thanks for the update, it's rare to see this kind of follow up.
  • jpshook
    jpshook almost 13 years
    @Arnis - Just to clarify, you are recommending to call ORM directly from your objects? So, no point to making your POCOs persistance ignorant...?
  • Arnis Lapsa
    Arnis Lapsa almost 13 years
    @Developer no, not exactly. they still should be persistence ignorant. you retrieve aggregate root from outside and call method on it that does the job. my domain model has zero references, just standard .net framework ones. to achieve that, You must have rich domain model and tools that are smart enough (NHibernate does the trick).
  • jgauffin
    jgauffin almost 11 years
    By using the orm directly you move the persistance responsibility (dealing with lazy loading etc) AND having correctly formed queries to the code that uses the OR/M. imho that adds those responsibilites (breaking SRP) to your method which wanted to fetch the entities.
  • Arnis Lapsa
    Arnis Lapsa almost 11 years
    @jgauffin every time you fetch an aggregate, you are in different context. every time smthg else needs to be eager loaded. eager loading concern alone tells that there is no single responsibility - it's not just about fetching an aggregate. hence trying to abstract away ORM might seem like pretending there is only one. and most ORM`s already have interface that "acts as an entity list" which basically is repository pattern. trick is to draw the line correctly - to recognize needs and options, then solve them with simplest solution possible.
  • jgauffin
    jgauffin almost 11 years
    You have too big aggregates if you need to only fetch parts of it.
  • Arnis Lapsa
    Arnis Lapsa almost 11 years
    @jgauffin You have too much aggregates if you need to fetch more than one of it
  • jgauffin
    jgauffin almost 11 years
    On the contrary. Fetching more than one ggregate often means that you can use the PK or indexes. Much faster than taking advantage of the relations that EF generates. The smaller root aggregates, the easier to get performance on them.
  • Mark
    Mark almost 2 years
    @ArnisLapsa the links are dead in your answer could you see alternatives OR fill in the gist of the links?