Night of the living Repositories

time to read 8 min | 1506 words

This is a response to Greg’s post in reply to mine. I can’t recall the last time I had a blog debate, they are usually fun to have. I am going to comment on Greg’s post as I read it. So this part is written before I did anything but skim the first paragraph or so.

A few things that I want to clarify:

    • His post was originally intended to be an “alternative to the repository pattern” which he believes “is dead”.
    • Of course this is far from a new idea

First, careful re-reading of the actual post doesn’t show me where I said that the repository pattern is dead. What I said was that the pattern doesn’t take into account advances in the persistence frameworks, and that in many cases, applying it on top of existing persistence framework don’t give us much.

The notion of query objects is also far from my invention, this is just to clear that out, it is a well established pattern that I am particularly fond of.

Now, let us move to the part that I really object to:

What is particularly annoying is the sensationalism associated with this post. It is extremely odd to argue against a pattern by suggesting to use the pattern eh? The suggested way to avoid the Repository pattern is to use the Repository pattern which shortening the definition he provided

Provides the domain collection semantics to a set of aggregate root objects.

So now that we have determined that he has not actually come up with anything new and is actually still using repositories let’s reframe the original argument into what it really is.

I fail to see how I am suggesting the use of the repository pattern in my post. This seems to be a fairly important point in Greg’s argument. And no, I don’t follow how I did that. Using the approach that I outlined in the post, there is no such thing as repository. Persistence concerns are handled by the persistence framework directly (using ISession in NHibernate, for example), queries are handled using Query Objects.

The notion of providing in memory collection semantics is not needed anymore, because that responsibility is no longer in the user code, it is the responsibility of the underlying persistence framework. Also note that I explicitly targeted the general use of the repository, not just the DDD use of it.

The problem here is that the Repository interface is not necessarily Object Oriented. The Repository represents an architectural boundary, it is intended to be a LAYER/TIER boundary. Generally speaking when we define such interfaces we define them in a procedural manner (and with good cause).

Hm, I can see Greg’s point here, but I am not sure that I agree with him here. I would specify it differently .Service boundaries are procedural (be it RPC or message based, doesn’t matter). But a service spans both layers and tiers, and I am not going to try to create artificial boundaries inside my service. And yes, they are artificial. Remember: “A Repository mediates between the domain and data mapping layers…”

A repository is a gateway to the actual persistence store. The persistence store itself may be another service, it is usually a remote machine, and the interface to that is by necessity pretty procedural. Trying to model a repository on top of that would by necessity lead us to procedural code. But that is a bad thing.

The problem is, again, we aren’t attempting to take advantage on the capabilities of the persistence frameworks that we have. We can have OO semantics on top of persistence store, because the responsibility to handle that is in the hands of the persistence framework.

Analyzing the situation given of a CustomerRepository what would happen if we were to want to put the data access behind a remote facade?

I am going to call YAGNI on that. Until and unless I have that requirement, I am not going to think about that. There is a reason we have YAGNI. And there is a reason why I don’t try to give architectural advice without having a lot more context. In this case, we have a future requirement that doesn’t really make sense at all.

What would happen though if we used the “other” Repository interface that is being suggested? Well our remote facade would need to support the passing of any criteria dynamically across its contract, this is generally considered bad contract design as we then will have great trouble figuring out and optimizing what our service actually does.

If I need to make the data access remotely, then I am still within my own service (remember, services can span several machines, and multiple applications can take part of the same service). As such, I know exactly what the requirements and the queries that my remote data access is going to require. More than that, within a  service, I want as much flexibility as I can get.

It is on a service boundary, that I start pouring out concrete and post the armed guards.

I also think that there is some sort of miscommunication, or perhaps, as usual, I split a thought across several posts, because a few posts after the post Greg is talking about, I suggested just what he is talking about.

If you don’t want a LAYER/TIER boundary don’t have one just use nhibernate directly …

That is what I am advocating. Or Linq to Sql, or whatever ( as long as it has enough smarts to support what you need without doing nasty things to your code).

My whole argument is that the persistence framework is smart enough today that we don’t need to roll this stuff by hand anymore!

At this point you probably shouldn’t have a domain either though …

And I call bull on that. Repositories != domain, and how you do data access has nothing to do with how you structure your application.

Something that me & Greg & Udi have discussed quite often in the past is the notion of Command Query Separation. I’ll let Greg talk about that, and then add my comments:

I have had this smell in the past as well but instead of destroying the layering I am building into my domain (with good reason, see DDD by Evans for why) I went a completely different route. I noticed very quickly that it was by some random chance that my fetch plans were being different. I had a very distinct place where things were different, I needed very different fetching plans between when I was getting domain objects to perform a writing behaviour on them as opposed to when I was reading objects to say build a DTO.

Well, yes & no. There are quite a few scenarios in which I want to have a different fetch plan for writing behavior even when using CQS. But before I show the example, I want to point out that it is just an example. Don’t try to nitpick this example, talk about the generic principle.

A simple example would be a shopping cart, and the following commands:

  • AddProduct { ProductId = 12, Quantity = 2}
    • This require us to check if the product already exists in the cart, so we need to load the Items collections
  • Purchase
    • We can execute this with properties local to the shopping cart, so no need to load the items collection, this just charge the customer and change the cart status to Ordered

As I said, this is a simple example, and you could probably poke holes in it, that is not the point. The point is that this is a real example of real world issues. There is a reason why IFetchingStrategy is such an important concept.

That is leaving aside that like all architectural patterns, CQS is something that you shouldn’t just apply blindly. You should make a decision base on additional complexity vs. required scope before using it. And in many applications, CQS, or a separate OLTP vs. Reporting models, are often not necessary.

And yes, they are not big applications, with a lot of users and a lot of data, but they are often important applications, with a lot of complexity and behavior.