Ayende @ Rahien

Refunds available at head office

Repository is the new Singleton

I mentioned in passing that I don’t like the Repository pattern anymore much, and gotten a lot of responses to that. This is the answering post, and yes, the title was  chosen to get a rise out of you.

There are actually two separate issues that needs to be handled here. One of them is my issues with the actual pattern and the second is the pattern usage. There most commonly used definition for Repository is defined in Patterns of Enterprise Application Architecture:

A system with a complex domain model often benefits from a layer, such as the one provided by Data Mapper, that isolates domain objects from details of the database access code. In such systems it can be worthwhile to build another layer of abstraction over the mapping layer where query construction code is concentrated. This becomes more important when there are a large number of domain classes or heavy querying. In these cases particularly, adding this layer helps minimize duplicate query logic.

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

Note that while the actual pattern description defined in PoEAA and DDD are very nearly identical, the actual reasoning behind it is different, and the DDD repository is limited to aggregate roots only.

So, what is the problem with that?

The problem with this pattern is that it totally ignores the existence of mature persistence technologies, such as NHibernate. NHibernate already provides an illusion of in memory access, in fact, that is its sole reason of existing. Declarative queries, check. OO view on the persistence store, check. One way dependency between the domain and the data store, check.

So, what do I gain by using the repository pattern when I already have NHibernate (or similar, most OR/M have matching capabilities by now)?

Not much, really, expect as additional abstraction. More than that, the details of persistence storage are:

  • Complex
  • Context sensitive
  • Important

Trying to hide that behind a repository interface usually lead us to a repository that has method like:

  • FindCustomer(id)
  • FindCustomerWithAddresses(id)
  • FindCustomerWith..

It get worse when you have complex search criteria and complex fetch plan. Then you are stuck either creating a method per each combination that you use or generalizing that. Generalizing that only means that you now have an additional abstraction that usually map pretty closely to the persistent storage that you use.

From my perspective, that is additional code that doesn’t have to be written.

Wait, I can hear you say, but repositories encapsulate queries, and removing query logic duplication is one of the reasons for them in the first place.

Well, yes, but encapsulation of queries should be done in the repository. Queries are complex, and you want to encapsulate them in their own object. In most cases, I have something like this:

image

GetQuery takes an ISession an return ICriteria, which mean that my code gets the chance to set paging, ordering, fetching strategies, etc. That is not the responsibility of the query object, and trying to hide it only add additional abstraction that doesn’t actually give me anything.

I mentioned that I have two problems with the repository pattern, the second being the way it is being used.

Quite frankly, and here I fully share the blame, the Repository pattern is popular. A lot of people use it, mostly because of the DDD association. I am currently in the opinion that DDD should be approached with caution, since if you don’t actually need it (and have the prerequisites for it, such as business expert to work closely with or an app that can actually benefit from it), it is probably going to be more painful to try using DDD than without.

More than that, the way that most people use a Repository more closely follows the DAO pattern, not the Repository pattern. But Repository sounds more cool, so they call it that.

My current approach for data access now is:

  • When using a database, use NHibernate’s ISession directly
  • Encapsulate complex queries into query objects that construct an ICriteria query that I can get and manipulate further
  • When using something other than a database, create a DAO for that, respecting the underlying storage implementation
  • Don’t try to protect developers

Let us see how many call for my lynching we get now…

Comments

Colin Jack
04/17/2009 04:22 PM by
Colin Jack

To me Evans explained the reasons he believes you should work with aggregate roots in the book and I also value the contract they provide.

I personally find repositories very cheap to write other than for more complex queries where the real cost is in interfacing with NHibernate, so to me using them even for a fairly simple domain model is a bit of a no-brainer but I see your point.

"I am currently in the opinion that DDD should be approached with caution, since if you don’t actually need it (and have the prerequisites for it, such as business expert to work closely with or an app that can actually benefit from it), it is probably going to be more painful to try using DDD than without."

Couldn't agree more, a lot of people seem to ignore the fact that Evans makes this clear in the book.

sirrocco
04/17/2009 04:24 PM by
sirrocco

Does anyone have a link to a project that uses something like LatePayingCustomersQuery ?

Cause I just don't get it :-<￿

Paco
04/17/2009 04:25 PM by
Paco

I read both PoEAA and the blue DDD book, the best I could make of it, is a generic repository that takes specifications. That is limiting when you want to use reporting projections, but usable for all other situations.

Example:

IRepository <product repository = new IRepository <product(dependencies);

ISpecification <product spec = new CheapProductSpec();

IPagedList <product products = repository.GetPagedList(spec, Eager.LoadWith(product => product.Images);

This is still the repository pattern, but the difference with the implementations made in Kobe is that you don't need to change it, and don't need to make an implementation per aggregate root.

I don't need to inherit or change my generic repository. I just create the mapping and specifications.

Jan Willem
04/17/2009 04:28 PM by
Jan Willem

No need for lynching as far as i am concerned. I am experiencing the problems with the Repository pattern that you are describing. Thanks for the better alternative, which you should name in a pattern. (like "Intelligent Query")

BjartN
04/17/2009 04:44 PM by
BjartN

I like your perspective on this subject. And as always, It does make sense.

josh
04/17/2009 04:45 PM by
josh

I laughed a little at the title because I've used repo pattern and still carefully use singletons. And my experience with repository can be summarized by this: "Danger - Method Overkill", which is what you point out by the tendency to have methods for each query combination.

nHib is great, and I don't feel the need to abstract much more than configuration. Perhaps the Kobe team didn't understand MVC and ORM. Theorizing that they resent ORM seems a little presumptive.

Ayende Rahien
04/17/2009 04:45 PM by
Ayende Rahien

sirrocco,

Here is a typical implementation:

public class LatePayingCustomerQuery

{

public DateTime? DueDate {get;set;}

public decimal? MinAmount {get;set;}

public deciaml MaxAmount {get;set;}


public ICriteria GetQuery(ISession session)

{

    //overly simplifed query, but you should get the point



    var crit = DetachedCriteria.For(typeof(Payment))

            // actual late paying query logic

            .Add(Restrictions.Eq("IsLate",true))

            .SetProjection(Projections.Property("Customer.Id"));


    if(DueDate != null)

        crit.Add(Restrictions.Ge("DueDate", DueDate.Value));


    if(MinAmount != null)

        crit.Add(Restrictions.Ge("Amount", MinAmount.Value));


    if(MaxAmount != null)

        crit.Add(Restrictions.Ge("Amount", MaxAmount.Value));



    return  session.CreateCriteria(typeof(Customer))

                .Add(Subqueries.PropertyIn("Id", crit));

}

}

Anonymous
04/17/2009 04:55 PM by
Anonymous

You can use something similar with your Repository, without ending up with millions of querying methods.

Repository.Query(Specifier);

And specifier is a class with appropriate querying parameters.

public class Specifier

{

public DateTime? DueDate {get;set;}

public decimal? MinAmount {get;set;}

public deciaml MaxAmount {get;set;}

}

Andre Loker
04/17/2009 05:00 PM by
Andre Loker

Interesting posts, thanks Ayende.

I have some questions:

  1. What I like about repositories is the ease with which they can be stubbed or mocked. How do you handle that when you use ISession directly and the XYZQuery? Doesn't this make stubbing more complex?

  2. Also, what about cases where you have multiple DBs and thus multiple ISessions. Using repositories you can easily abstract away from that. How would you handle that elegantly and transparently?

Somehow I have the feeling that setting up the queries in the service layer can lead to a violation of the SRP. Then again, maybe that's precisely what the service layer is about. I totally agree that using repositories has the tendency that you keep adding yet another method to the repo just to handle that one special case. And in the end you don't know what all you GetCustomerWithXYZ do.

So I think I will give it a try and play with your approach.

BTW: I like the title of your post :-)

Ayende Rahien
04/17/2009 05:02 PM by
Ayende Rahien

Anonymous,

But why would I want to do that? What is the benefit of adding this level of abstraction?

And who is going to translate the specifying to the actual query?

Peter Morris
04/17/2009 05:04 PM by
Peter Morris

Eric Evans does mention the "Specification pattern" which can be used to fetch objects using criteria you pass as object instances.

The main reason I like repositories is I find them dead simple to mock in tests.

var user = new User();

var m = MockRepository.GenerateMock <iuserrepository();

m.Expect(x => x.GetByEmailAddress("me@home.com")).Return(user);

me
04/17/2009 05:06 PM by
me

I just use it for abstraction, so I can change my ORM when something better comes along.

Andrew Hallock
04/17/2009 05:13 PM by
Andrew Hallock

This is exactly what my code has evolved to. Queries are first class objects.

@me

That doesn't seem practical or worth the effort. It would take serious effort to come up with something better than NHibernate.

meisinger
04/17/2009 05:17 PM by
meisinger

i think that it would be more fair to say that you don't implement the Repository pattern much any more

you still use the Repository pattern but you use it via NHibernate

NHibernate deals with the mapping, translation, and management of your persistant objects or entities. in my mind, if you are using an ORM then you are (for the most part) using the Repository pattern

now whether or not you are following the DDD Repository pattern and dealing only at the Aggregrate Root level is another story all together. in those instances you more than likely would have to implement a Repository pattern to ensure proper management

but, to your point, i think the many developers use the term "Repository" when they are actually implementing a gateway or data access object

it is my opinion that most projects should focus more on fetching and strategy patterns than the repository pattern while still abstracting away the Create, Update, and Delete methods

Steve
04/17/2009 05:20 PM by
Steve

"the way that most people use a Repository more closely follows the DAO pattern, not the Repository pattern. But Repository sounds more cool, so they call it that."

Question....

"A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection"

Could that mean you might have a 'Repository' that uses a set of 'Dao' objects ?

alberto
04/17/2009 05:24 PM by
alberto

Love the title.

Definitely, there is not such a thing as good practice. Not too long ago Greg posted this.

So, who uses those query objects? I'd like to see a more complete sample (pun intended ;)), too.

Rub
04/17/2009 05:36 PM by
Rub

About title ... The repository usage instead of singleton's allows us to write our tests simple.

for this reason, singleton is antipattern, repository isnt :-)

Jimmy Zimmerman
04/17/2009 05:46 PM by
Jimmy Zimmerman

RE:

It get worse when you have complex search criteria and complex fetch plan. Then you are stuck either creating a method per each combination that you use or generalizing that. Generalizing that only means that you now have an additional abstraction that usually map pretty closely to the persistent storage that you use.

This is why Expression trees are so important. They are the missing part of the equation wherein the best parts of repositories (eg TOTAL PI) meets with the best of ORMs (complex associations and queries without overload hell).

Justin Etheredge
04/17/2009 05:52 PM by
Justin Etheredge

As always, I think the answer is that "it depends". There are many factors that could control which direction you go. If you need strict control over what queries are being fired against the database, then a repository may be what you need.

On the other hand, if flexibility and agility are your primary concern, then exposing the query layer in your services may be doable for you. I know that it would certainly speed up development. The problem is that you offer creating classes to abstract queries, but in all but the most complicated queries, that seems like it would be more work than creating a method per query.

Shane Courtrille
04/17/2009 06:02 PM by
Shane Courtrille

re: Complexity of creating queries.

Not necessarily. Using ReSharpers file templates you can kick out a new query class very quickly. The development cost would probably depend on how you are retrieving your query instance..

Rob
04/17/2009 06:08 PM by
Rob

With IQueryable support IRepository basically becomes IQueryable + insert and delete. It actually works out pretty nicely. You have 1 generic repository and your query definitions just become extension methods mapping IQueryable to IQueryable.

Something like:

public static IQueryable[Customer] WhoAreLatePaying(this IQueryable[Customer] customers, .. )

{

return customers.Where(c => stuff);

}

These are all nicely testable and persistence framework independent (as long as the persistence mechanism supports IQueryable). Right now this just means Linq to SQL and EF (roughly), but it seems like Linq to nHibernate will be there soon.

zvolkov
04/17/2009 06:24 PM by
zvolkov

I agree that multiple repositories are evil, as are container-aware services that must be container-aware so they can ask the container for an IRepository <order. In my last project I use single lighweight repository that wraps ISessionFactory and provides CRUD&L methods plus a bunch of "query object" classes that go as arguments into the List method. My IRepository gets injected into my services by the IoC container, and I can mock it in my unit tests if I don't feel like using in-memory SQLite. My only problem is I can't transparently go to a different database based on the type of object I want to work with, which was so easily done with multiple repositories. I guess I could switch to directly injecting ISessionFactory to my services like Oren seems to suggest above but then I won't be able to mock the CRUD operations easily. Perhaps I should go 100% in-memory DB instead of mocking?

Stephen
04/17/2009 07:01 PM by
Stephen

Wow I thought I was a freak for really disliking the abstraction that repository gives, most of all it tends to be a really hard abstraction to meet, leaving you with a really oddly shaped repository that probably still 'taints' the depending code.. I've also followed the same pattern, based on being told 'the repository lets you standardize queries, and avoid repeated query behavior all over the app'... uh, I can do that without the repository? and pretty cleanly today.

J&#233;r&#233;mie
04/17/2009 07:04 PM by
Jérémie

After experimenting Domain Driven Design and its patterns on a large project, I came to the Command Query Separation conclusion..

No 'Find..' methods on the repository. No need for complex things with presentation concerns.

The repository should only provide what's needed to change the state of your domain.

Then you can have a flexible query interface that support IQueryable or NH query objects, that can even get data from percomputed tables or a cube...

Domain Driven Design patterns are about domain mutation mainly and focus on that.. That's the hard part on complex domains.

I you just need CRUD, then a repository is not required.

Benny Thomas
04/17/2009 08:16 PM by
Benny Thomas

I totally hate your LatePayingCustomerQuery implementation. It screams fluent Quert builder towards me.

I feel that the Detached Criteria should be contructed in the default constructor.

Why should you have a getters on the properties and why doesn't the settes update the Detached Criteria on the fly. The setters should have been methods prefixed with With.

This would have made the if tests redundant and the whole thing more readable.

var query = new LatePayingCustomerQuery().WithDueDate(...).WithMinAmount(...).WithMaxAmount(...).BuildQuery(session);

I know that there is a movement against fluent builder, but in some cases I feel they give a better readability to the code.

But as an example your code works.

Morten Lyhr
04/17/2009 08:30 PM by
Morten Lyhr

Amen...

Making "an database table" == "a collection", just dosent make sense. Its leaky...

More over often I just need a few columns from 2 or more tables, what repository does that belong to? What if its from different agregate roots?

Making distinct named queries, and sticking them in a Queries Folder/namespace is simple and its easy for every one too find.

Delete/Update/Insert kan be handled directly by the NHibernate ISession/Linq2Sql DatabaseContext. We might even use ISP, and only expose what we need through an interface.

There is a distinct difference between quering and modifying,

And no a repository does not make that go away, but it does offer a posibility to create a lot of boiler plate code.

Stiiifff
04/17/2009 08:52 PM by
Stiiifff

Isn't the Repository supposed to expose collection-like methods? Using Linq to represent the queries in a reusable & abstract way would be a nice solution IMHO. If it doesn't suffice then it means u probably doing OLAP-type of queries and u should probably be using SQL.

Nick Gieschen
04/17/2009 08:58 PM by
Nick Gieschen

I fully agree most of the time you see a repository it is just a renamed DAO.

The case for a repository is when you get to DDD. If you want to enforce in code the maintenance of the integrity of aggregates, you need them. Moreover, there are times when you reconstitute an entity that you need to do things to it other than just getting it from the database. A repository is useful as a location to do these things.

CaliCoder
04/17/2009 09:08 PM by
CaliCoder

@Anonymous You've made a query criteria repository... doesn't that already exist? isn't it called SQL or HQL or whatever 'QL you happen to be leveraging to define the scope of your query?

@Benny you're arguing semantics which is always fun (because it's hard to make an invalid argument about semantics)

I got your back Ayende so less reputes required haha!

Ray
04/17/2009 09:09 PM by
Ray

We're moving to using IRepository <t over linq to sql at work and it's working fine. (Of course, this is coming from the 'big ball of mud' so anything is better at this point.) I agree with Davy's post re: protecting developers, but I think the repository pattern is very easy to 'get' for someone not otherwise inclined to think in terms of abstractions, as well as being testable (win win).

Peter Morris
04/17/2009 09:12 PM by
Peter Morris

I was really under the impression that you should use the Specification pattern for complex repository requirements.

For simple requirements have 3 or 4 methods for retrieving, anything more complicated go for the Specification approach.

I think one of the purposes too is to make queries like IOrderRepository

IEnumerable <order GetOwnedByCustomer(int customerID)

To avoid 2 way associations.

CaliCoder
04/17/2009 09:33 PM by
CaliCoder

@Benny I should revise my post: What I meant to say is "it's hard to make an invalid argument about semantics [unless you're writing an LR parser... then it's all about semantics]" getter/setter/with semantics... whatever helps you sleep at night =)

@Peter IMHO (the 'H' is probably debatable at this point) The wiki entry kind of makes my point for me http://en.wikipedia.org/wiki/Specification_pattern

Chaining AND and OR, etc together is a very fundamental concern that is handled by your ORM framework. I'd even prefer writing raw SQL over using that pattern. You're going to ensure compilation by using the pattern, but that's about where it ends. Looking at the UML diagram on the wiki (which is a pretty simple example) that thing is starting to look like a parse tree. And for me I wouldn't even dare approach that problem in my domain logic... maybe you could if you're a ninja coder and like a challenge though

J&#233;r&#233;mie
04/17/2009 10:06 PM by
Jérémie

I think things get a bit confuse here...

There are 2 aspects of the repository pattern.

As a Object Oriented pattern, the repository is just a way to hide persistance concerns behind a collection-like interface.

The main advantage here is to gain persistance ignorance. Test in isolation are far easier to write.

(be carfull though, IQueryable doesn't hide completly implementation, some providers don't implement everything... and you'll only notice when runing the code.

This kind of repository can be used in simple DAO style.

When it comes to Domain Driven, the problem is a bit different. The repository has a Domain meaning. It's here to persist the domain state in a domain oriented way. It should be focused on domain state mutation.

The problem is that queries often span across several different entities/aggregate roots. It's why you must have a separate way to query your domain. A way that is more flexible. But that's not the role of the repository.

When it comes to

CaliCoder
04/17/2009 10:17 PM by
CaliCoder

Ayende, what do we call your invention? I propose the Class per Query pattern

Dmitry
04/18/2009 12:32 AM by
Dmitry

What does everything think about repositories that provide a unified way to query retrieve and persist data in multiple heterogeneous data sources?

I don't think it's a good idea to initialize data contexts and data services from query classes directly. It also seems to match Fowler's definition of repositories being mediators.

Ayende Rahien
04/18/2009 03:44 AM by
Ayende Rahien

Cali,

Um, query object pattern?

Fabio Maulo
04/18/2009 04:25 AM by
Fabio Maulo

LOL

I'm using DAO because it is not so cool and because I want be able to say "good bye" to NH and/or use ADO NET where needed and/or encapsulate a dataAccess that don't use RDBMS.

Another "thing" I'm using is a "Model" (my concept of Model that is basically the Use-Case); over the "Model" put what you want.

Fabio Maulo
04/18/2009 04:29 AM by
Fabio Maulo

Ah... the DAO work with something named Criteria (more than one where needed); the Criteria is not the ICriteria, instead it is my own Criteria or better more is the one o more Criteria accepted by a specific DAO contract.

Krzysztof Kozmic
04/18/2009 06:07 AM by
Krzysztof Kozmic

@Fabio,

You posted about the main issue I have with Ayende's approach.

One advantage repositories give you is they are a simple interface that has no ties to any specific data access technology.

With query object pattern, query object is anything but specific technology ingnorant.

Perhaps I'm missing something here, but how do you use that in the greater context, so that NHibernate does not leak up the layers? Where do you draw the line for NHibernate? How do you test classes depending on such query objects?

Krzysztof Kozmic
04/18/2009 06:08 AM by
Krzysztof Kozmic

And regarding my previous comment.

Everything after "You posted about the main issue I have with Ayende's approach." is @Ayende

Sorry for any confusion.

Benny Thomas
04/18/2009 07:07 AM by
Benny Thomas

@CaliCoder: It's all about semantics, how do else get maintainable/clean code. Just look at Kobe, Ayende's favourite sample application.

Why should have a getter, If you never use it? It's kind of Yagni. But if you want, you can call it semantics....what ever works for you ;)

CaliCoder
04/18/2009 07:40 AM by
CaliCoder

@Benny i see your point there. probably wouldn't need the getter... i can't think of an edge case where you might need it but maybe there is one out there?

Torsten Schmidt
04/18/2009 09:17 AM by
Torsten Schmidt

@Ayende: I think I can partially understand your resentment against the repository pattern, but I don't really see the solution for this yet.

Your LatePayingCustomerQuery sample looks fine so far, but how is this used in the code? Let's say you have a plain MVP/MVVM WPF application to show a list of late paying customers. Where does LatePayingCustomerQuery live and how does the presenter get the list of late paying customers? Does the presenter use LatePayingCustiomerQuery directly like:

using (var unitOfWork = SessionManager.StartUnitOfWork())

{

var lpcq = new LatePayingCustomerQuery();

lpcq.DateTime = <whatever;

lpcq.MinAmount = <whatever;

lpcq.MaxAmount = <whatever;

var criteria = lpcq.GetQuery(unitOfWork.session);

_listOfCustomers = criteria.List();

}

? ...doesn't feel right to me. You would at least need to hide this in some kind of service layer and then I don't see much of a difference compared to using a repository anymore:

using (var unitOfWork = SessionManager.StartUnitOfWork())

{

_listOfCustomers = 

_customerRepository.GetLatePayingCustomers(

<whatever, <whatever, <whatever)

}

_customerRepository is injectable / mockable and hides the ICriteria / ISession stuff which IMHO should not be visible in any higher application level. It makes unit testing the repository itself and its client code easy-peasy.

If ICustomerRepository has 20 overloads for retrieving subsets of customers, than this might be not very beautiful but it's no extra burdon either. You can (and should) still refactor your queries into query objects like LatePayingCustomerQuery making the ISomethingRepository interface becoming just a simple single accesspoint for those queries.

Maybe it's just entrenched habit to use repositories and I completely miss the point. It would be nice to see some edge-to-edge code for the "query object pattern" solution you propose.

Jason
04/18/2009 12:27 PM by
Jason

How timely! Recently, I was responsible for setting up the data access layer for the dev team where I work, with the help of our solutions architect. We built it on top of NHibe largely in order to avoid methods like

GetAccount(int id);

GetAccount(string userId, string email);

We wanted to parameterize everything and write O(1) methods for n classes.

There were 2 reasons that came up to write the occassional GetSomeTypeWhereSomethingEquals(someValue):

  1. Some of the more complex queries that are called multiple times in the app are easier to use if they have a single method in the DAL representing them--it is more convenient.

  2. Between working with 2 legacy databases that many legacy systems depend on and not having complete mastery of NHibernate, we simply did not know how to map BL classes directly to the DB tables in NHibe alone, despite our efforts. So, we wrote some methods for specific types in the DAL.

The main drawback to #2 is that our generic Get <type(id), FindAll <type(where), Set(obj), and Delete(obj) was that you sometimes had to stop and review the DAL documentation to see what exactly happens if you call Get <account(x) vs. GetAccount(x) to know what you're doing.

Another big drawback to the DAL was that it did not let you control connections, caches, or transactions independently of each other. The first two don't bother me, but not being able to control transactions will likely come back to haunt us unless we add a feature whereby we the app can control it.

In the end, there are pros and cons to this extra layer. I feel its biggest benefits are:

-not committing to a particular 3rd-party data access thingy (What if I want to switch to another API that is built on NHibe?)

-managing multiple ISessions for multiple databases

-managing complex performance-related concerns (e.g. keeping ISessions around, connecting and disconnecting, 1st-level cache management).

-working with stubborn legacy db's

Finally, I question if NHibe is even the right tool for working with legacy DB's. For our project, it was far better than no ORM at all, but iBatis might be better still... I don't know until I try.

Rafal
04/18/2009 01:12 PM by
Rafal

IRepository <orderrepository =

new ValidatingOrderRepository(

new SecurityRepository

<order(

  new LoggingRepository

<order(

   new CachingRepository

<order(

    new NHibernateRepository

<order()))));

Ayende, do you remember that code? It's from your MSDN article. No wonder you got fed up with repository pattern :)

Jason
04/18/2009 01:16 PM by
Jason

"We wanted to parameterize everything and write O(1) methods for n classes."

I should clarify that O(1) is the number of methods, not how long they take to run. A more typical repository-based DAL has O(n*m) methods where n is the number of classes and m is the number of specialized queries you want to do with those classes, which is usually related to the number of properties of a given class.

Ayende Rahien
04/18/2009 01:20 PM by
Ayende Rahien

Rafal,

I most certainly remember this. I was a big fan of the Repository, I think that I mention this in the post as well.

That is why I feel confidant to criticize it.

Remco Ros
04/19/2009 02:56 PM by
Remco Ros

@Ayende

As Fabio and Krzysztof state, how do you prevent NHibernate (or other data access code) to leak up the layers.

It should be possible to let a DI container manage the queries (like repositories), but that doesn't feel right to me.

Ayende Rahien
04/19/2009 03:03 PM by
Ayende Rahien

Remco,

I don't try to stop that. I think that it is a good thing that it does. It let me use the most of what NH can offer me.

Hendry Luk
04/20/2009 04:07 AM by
Hendry Luk

I'm totally with Peter Morris regarding the ease of mocking the good old repository.

m.Expect(x => x.GetByEmailAddress("me@home.com")).Return(user);

Specification, or Query object, or Pipe/Filter queryable make it much harder to mock out querying mechanism. The prime culprit is "new" keyword (or extension method or other static method alternatives).

J&#233;r&#233;mie
04/20/2009 02:25 PM by
Jérémie

If some of you are disturbed by the GetCustomerById methods, just remember you're using C# and that you can do things like :

interface ICustomerRepository : IEnumerable <customer
{

 Customer this[int id];

 Customer this[string reference];

}

then you can write things like :

ICustomerRepository customers = ...;

Customer customer = customers[42];

Customer otherCustomer = customer["C3P0"];

But this should be only used for domain purpose, not for querying (see remarks above...)

Ayende Rahien
04/20/2009 02:40 PM by
Ayende Rahien

Jeremie,

That ignore the distinction between Get & Load, though

J&#233;r&#233;mie
04/20/2009 02:47 PM by
Jérémie

What do you mean by 'distinction between Get & Load' ?

Ayende Rahien
04/20/2009 02:50 PM by
Ayende Rahien

I have a full post on it that would air in the 30th.

Or, go and read the docs about ISession.Get vs ISession.Load

J&#233;r&#233;mie
04/20/2009 03:00 PM by
Jérémie

of course read 'weak relations' instead of 'week relations' in the previous comment...

George Mauer
04/20/2009 04:32 PM by
George Mauer

From what I understand, part of your argument revolves on the contention that its silly to treat all Customers as an in memory collection and all Products as a separate one when you can just treat all objects as being in memory - something that ISession already does.

Here's my problem though: I eschew the use of a repository for straight up ISession and ICriterion but then discover that in the legacy database I'm working with persisting my Customer object is either impossible with NHibernate or difficult to the point of I'm-not-going-to-have-the-time-to-learn. Or perhaps it needs to be persisted via web-service rather than the db.

If I'm using the repository pattern I can inject the RawSqlCustomerRepository or WebServiceCustomerRepository implementations instead of NHibernateCustomerRepository. I know only very basic NH so there could easily be something that I'm missing but in your solution would it have to be a custom ISession full of switch statements and NotImplementedExceptions?

What if we're talking about using an ORM other than NHibernate? How hard is it then?

I have found the repository pattern encourages ORM use on a practical level when non-gurus are involved. For example my current project with an inexperienced team I can say "let's try to do it with NHibernate, if that turns out to be too difficult, we'll just implement the repository with something that you're familiar with like Ado.NET or stored procedures". Much easier to get buy-in, and far easier to convince them not to worry about data access, as you say "There is no database".

That being said, I DO find myself suspicious of how repository seems to have taken over the world...

Gunnar Liljas
04/20/2009 11:10 PM by
Gunnar Liljas

Another issue with the Repositories, when used as DAOs (the distinction is often hard to make) is that abstracting them into interfaces (such as IRepository <t) can introduce a dependency misrepresentation. There may be an ICustomerRepository and an IOrderRepository, and their abstract nature is useful for the application and its development/testing. However, they're often not as interchangeable as they seem to be, since Customers coming from an NHibernateCustomerRepository will only handle Orders coming from itself or an NHibernateOrderRepository. Having repositories only for aggregate roots is a good solution, but it isn't always possible.

Of course this is more of a general trade-off with abstractions, but abstract repositories are often "marketed" with the "lets you seamlessly switch from a RawSqlCustomerRepository to RawSqlCustomerRepository" argument, and in reality it's just not true.

The "query object pattern" is really good, but it's not exactly new. I've used it for a long time to be able to save user defined queries and it's also extensively used in the CMS I work with (EPiServer), where the implementation is exactly like Ayende's, except that the GetQuery method returns a HQL (yes, NHibernate) query instead of an ICriteria.

Gunnar Liljas
04/20/2009 11:12 PM by
Gunnar Liljas

Should have been "RawSqlCustomerRepository to WebServiceCustomerRepository"....

Ayende Rahien
04/21/2009 07:35 AM by
Ayende Rahien

Andre,

1) see my other post about mocking, I don't tend to use it too much

2) I probably wouldn't want to abstract that, mind you. I want to be aware of my data access pattern, not hide it.

Ayende Rahien
04/21/2009 07:36 AM by
Ayende Rahien

Steve,

That is how it is usually described

Ayende Rahien
04/21/2009 07:37 AM by
Ayende Rahien

Krzysztof ,

I don't try to draw a line

Ayende Rahien
04/21/2009 07:41 AM by
Ayende Rahien

George,

I think that you can probably find a way to make NHibernate deal with that.

Worst case scenario, you can tell NHibernate to save using a stored procedure.

Also, note that I kept qualifying what I said with "when using NHibernate", when using other data access methods that are not as sophisticated, other rules apply

Fregas
04/21/2009 02:24 PM by
Fregas

I like the repository pattern, but I don't see anything wrong with your query object either. You're solving the same problem in a different way. I do have the repositories set things like paging--maybe they get the max number of rows from a configuration object and get passed the current page their on by the UI layer. I also put the session building stuff in a base class all my repositories use.

Toe-may-toe, tuh-mah-toe

Fregas
04/21/2009 02:29 PM by
Fregas

I should have also added, for the complex sorting, paging, max rows, criteria, etc. I use a criteria method that is similar to your query object.

crit.CustomerType = CustomerType.Business;

crit.MaxRows = 25;

crit.PageIndex = 1;

crit.SortOrder = new[] { "Lastname asc", "firstname desc"}

repository.FindCustomer(crit)

George Mauer
04/21/2009 06:19 PM by
George Mauer

Ayende,

I agree that when you know ahead of time that NHibernate will be handling everything then you might as well use a single data access object such as ISession. When working with object databases you do roughly the same thing (at least with db4o you do).

However, If you admit this then the whole argument of "repositories are a passing fad" goes out the window. Most projects cannot (often for lack of confidence in their technical abilities but for many other reasons too) commit to ORM-for-everything. This is something that might change as ORMs evolve and acquire data providers to cover any reasonable scenario and people get more comfortable with their use but, for the foreseeable future, repositories are still the best solution to the problem.

Janus
04/23/2009 08:24 PM by
Janus

Hello Ayende @ Rahien

I totally understand your opinion, I've myself experienced similar peculiarities. I did start a project with the ohh so well know Repository-pattern using Linq to SQL, and as this wasn't enough I quickly jumped into the oh-so-great Generic Repository! (you posted a similar opinion earlier also S )

What a shame and waste of time, luckily I created a well-functioning prototype before showing my work to the team. (well. I did actually release a small sample to them)

However I dumped the generic approach into the drain. So now I ended up with those stupid methods as you pointed out. What was that? And why should I create such a method for each query - gee - don't waste my time with such old-style LOL

Well... as time is now, we still use the Repository-pattern, but I've modified it. There's only one method for retrieving data.: IQueryable <someobject GetData() and certainly no generic junk, why not? Because I need an easy control for deleting, updating and inserting - those methods exists inside the appropriate repositories. For instance for inserting child records I use the parent object -> Repository and inside this, because of the fact that the parent knows the childs, the parent also controls the data integrity.

Inside the Repository layer I also create the neccessary specifications as extension methods, so we can use them from the Service Layer.

One very important thing here, is to know that the specification really are very specific and not just a normal query. The more general queries we just create in the service layer and then just hook a specification in the end for some specific query-needs, of cause some of the extensions are overloaded.

As mentioned I understand your post, but I would be a bit frustrated if we just should create all our updating, inserts from the ServiceLayer, that would mean that the service layer should have knowledge of the database which I don't like.

The big question here... Would the world be a better place when we find another name for the Repository or are we going to respect the old guys with their DDD and their patent-taking of cool-sounding names ;-)

Jimmy Zimms
04/24/2009 06:25 PM by
Jimmy Zimms

I keep seeing a lot of talk in this arena regarding what is in reality free form queries for reporting purposes and nothing to do with any domain behavior. Not always but usually, IMHO, this is a seperate part of the domain (read: new objects). Make reporting concepts a first class citizen instead.

I've also noticed that for some reason I cannot understand that people somehow think that a specification, a query object, and a repository interface are all mutally exclusive. A repository is there to make domain query concepts explicit. This does not however that an intention based concept cannot be modeled in your application. This is where we've done lots of work with expression trees to expression the "what i want" not the "how to do it".

[shrugs]

Ryan.Magnusson
04/27/2009 08:33 PM by
Ryan.Magnusson

At first glance, and on [very] simple projects, the Repository Pattern (per PoEAA) can appear to be an unnecessary layer of abstraction and overhead. However, I have yet to work on an application where all of the data we require to create the domain objects resides in a single database. Especially when any accounting information/human-resource data is ever needed to match up against other Domain objects. The Repository Pattern allows the domain and data-services layers to be completely isolated by abstracting out any dependencies between the myriad data-access components that might be required per domain object. This abstraction also allows us to implement more stable solutions where the data is being supplied today by a database, but tomorrow can be easily swapped out by web-services from another party or in the cloud.

Bunter
05/20/2009 11:48 PM by
Bunter

Query objects are nice even if you have dao wtih Query(queryspec, pageinfo, sortinfo). I've usually compiled "repositories" that are actually DAO-s but they tend to grow longer than any class should be and have exactly the same signature issues you describe. Putting them out of data layer can sometimes introduce weird things - compiling query might be rather infested with knowledge of data model. Or is it just my models...

Vadim
06/09/2009 05:10 PM by
Vadim

What is the place for LINQ in this scheme in your opinion?

Ayende Rahien
06/09/2009 05:17 PM by
Ayende Rahien

The internal impl. of the query objects, maybe.

Comments have been closed on this topic.