To me, repositories, combined with ORM or other DB persistence layers, have these disadvantages:
- Covering up Units of Work. UoW have to be coded by the programmer and can rarely be implemented as a kind of magic in the background, where the user simply makes queries and modifications, without defining the UoW boundaries and possibly the commit point. Sometimes, the UoW are abandoned by reducing them into micro UoW (e.g. NHibernate sessions) in each Repository access method.
- Covering up, or, in the worst case, destroying Persistence Ignorance: Methods like "Load()", "Get()", "Save()" or "Update()" suggest immediate, single object operations, as if sending individual SQL/DML, or as if working with files. In fact, for example, the NHibernate methods, with these misleading names, usually don't make individual access, but enqueue for lazy load or insert/update batch (Persistence Ignorance). Sometimes, programmers wonder why they don't get immediate DB operations and forcibly break up persistence ignorance, thus killing performance and using major efforts to actually make the system (much!) worse.
- Uncontrolled Growth. A simple repository might accumulate more and more methods to fit specific needs.
public interface ICarsRepository /* initial */
ICar LoadCar(int id); // bad, should be for multiple IDs.
void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
public interface ICarsRepository /* a few years later */
ICar LoadCar(int id);
IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer);
bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
void SaveCar(ICar carToSave);
4. Danger of God object: you might be tempted to create one god class, covering all of your model or data access layer. The repository class would not only contain Car methods, but methods for all entities.
In my opinion, it is better to offer at least some query opportunities, to avoid the huge mess of many single purpose methods. No matter if it is LINQ, an own query language, or even something taken directly from the ORM (OK, kind of coupling problem...).