Ayende @ Rahien

Refunds available at head office

Ask Ayende: Life without repositories, are they worth living?

With regards to my quests against repositories, Matt asks:

For example, you dismiss the repository pattern, but what are the alternatives? For example, in an ASP.NET web application you have controllers. I do NOT want to see this code in my controllers:

var sessionFactory = CreateSessionFactory();

using (var session = sessionFactory.OpenSession()) { using (var transaction = session.BeginTransaction()) { // do a large amount of work

// save entities
session.SaveOrUpdate(myEntity);

transaction.Commit();

} }

That is ugly, repetitive code. I want in my service methods to Get, update, save, and not have to worry about the above.

This is a straw dummy. Set up the alternative as nasty and unattractive as possible, then call out the thing you have just set up as nasty and unattractive. It is a good tactic, except that this isn’t the alternative at all.

If you go with the route that Matt suggested, you are going to get yourself into problems. Serious ones. But that isn’t what I recommend. I talked about this scenario specifically in this post. This is how you are supposed to set things up. In a way that doesn’t get in the way of the application. Everything is wired in the infrastructure, and we can just rely on that to be there. And in your controller, you have a Session property that get the current property, and that is it.

For bonus points, you can move your transaction handling there as well, so you don’t need to handle that either. It makes the code so much easier to work with, because you don’t care about all those external concerns, they are handled elsewhere.

Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

Apostol Apostolov
01/19/2012 10:40 AM by
Apostol Apostolov

Doing my application development just like you proposed in the article. Build your infrastructure and everything is much easier from that point on. Thumbs Up!

zdeslav
01/19/2012 10:48 AM by
zdeslav

In my controllers, these examples typically look like this:

// do a large amount of work session.Save(myEntity);

everything else is hidden from the user (inside filters, modules, etc), and still no repository. if myEntity is already a persistent entity (not a new one), even the .Save line is not needed

zdeslav
01/19/2012 10:49 AM by
zdeslav

ooops, formatting... it should have been:

// do a large amount of work session.Save(myEntity);

zdeslav
01/19/2012 10:51 AM by
zdeslav

ok, I give up on trying to break this into 2 lines, but you get the point...

Dan
01/19/2012 10:57 AM by
Dan

zdeslav, use pastebin and link it here.

eatfrog
01/19/2012 11:02 AM by
eatfrog

two things i wonder about. how do you test the application with an unit testing framework when so little is abstracted away? how would you mock the database connection?

and the second thing that has become more common, you make a web application and later on some of the functionality has to be shared with some other application, mobile/desktop/whatever.

now you have a lot of business logic in the specific controllers? how would you deal with this scenario?

personally i like to keep the web app ignorant of all things database and BL. it increases the complexity, but i feel it is worth it.

Phillip
01/19/2012 11:09 AM by
Phillip

@eatfrog - A lot of the time that sort of stuff is CRUD, it's not real business logic. There's no real need to abstract CRUD into 15 layers.

eatfrog
01/19/2012 11:14 AM by
eatfrog

@Phillip, sure i agree, maybe 70-80% is just plain crud. but then you have the 20-30% that is not (in my use cases). But isn't it better to keep it all in one place? Just because you could do some crud in the web app and put business logic in some other place, it just feels wrong.

and consider something that initially was just crud, and later on you need somekind of validation on inserts. now you might have to look up every place where that insert is done?

Dave
01/19/2012 11:16 AM by
Dave

The benefit of using a repository (in a correct manner, not like in Northwind) is that you can swap NHibernate for EF or even RavenDB.

If you introduce ISession to your application you can not. Let's say you already have a big e-commerce website running on Nhibernate, but you want to migrate certain parts to RavenDB or a ServiceBus. All you have to do is to bind a new implementation of your repository to the interface. At least that is the whole intention of IoC.

Or am I getting a dinosaur by still following the SOLID principles? Not using repositories (or actually hiding data implementation behind a interface implementation) makes it very hard not to break the 'L' (Liskov Substitution) principle..

If you follow the SOLID principles you should be able to just change the data provider without touching any other code and the (web) application should still be working. If it's not, you've broken the principles..

tobi
01/19/2012 11:26 AM by
tobi

Dave, when did you last swap your data provider? ;-) Did it go well?

James McKay
01/19/2012 11:37 AM by
James McKay

I wish people would stop bringing up this old chestnut of swapping NHibernate for Entity Framework or RavenDB.

Besides the complete YAGNI factor, it doesn't save you any work. You'll either have to go for the lowest common denominator, or else you'll have to build an abstraction layer so complex that it's more work to maintain than rewriting your code in the first place should the need arise. Ayende covered this in some detail some time ago here: http://ayende.com/blog/4567/the-false-myth-of-encapsulating-data-access-in-the-dal

Seriously, that kind of thing takes you deep into Architecture Astronaut territory. Just pick one or the other and stick with it.

Allan
01/19/2012 11:44 AM by
Allan

Dave, how would your repository differ from that in the application that Ayende is reviewing?

Would you ever really consider swapping ORM's? How many legacy projects have you worked on and re-implemented the DAL? Good luck getting a business case approved for that one!

Toni
01/19/2012 11:49 AM by
Toni

It would be nice to see an example how to handle queries like:

session.QueryOver.Where(x => x.Customer == session.Load(customerId).OrderBy(x => x.Timestamp).

If I have that in my code then I need a way to mock it in unit tests. I can use sqlite but I don't want to use it for every test because I might be testing completely different thing. So for the sake of example let's say I have to perform three complex queries on different entities, then do something else and finally send an email. If my test only cares about the fact that email is sent then how do I handle the queries? It would seem like a lot of work to mock all those Where, OrderBy etc.

One way is to capsulate each query into own class and mock those but if I do that am I just reinventing the repository pattern?

Belvasis
01/19/2012 11:51 AM by
Belvasis

Hm...is this repository thing really worth the hole discussion? Everyone can do it simply the way he prefers. For me, i like it to abstract away the ORM in my app and provide a clear and simple interface to the developer. I call it simply IEntityAccess and for the most cases it does what we need from it. That means not that i try to completely hide the ORM. In the cases where i need features from NH i simply use it. This is no problem, since all data access happens in services or models. I also think it is difference if you build an ASP app or if you build an WPF/WinForm app. In the latter case i can't imagine how to build a working session management without any abstraction.So in the end i think the truth lies somewhere in the middle.

Mihai
01/19/2012 11:58 AM by
Mihai

Honestly, I've dropped the entire Repository pattern out the window a long time ago.

I just have a QueryService, SaveOrUpdateCommand and a DeleteCommand. I can extend the query service in any way needed, because it just returns an IQueryable.

If I need something more complex, I can always wrap, decorate or implement it whenever the need presents itself.

I can go back to the Repository pattern whenever, but it would just group the above services. It doesn't need to be any more complex than this.

Oh, and you always swap out the implementation at will, of course. The operations are always the same, either query or execute a command that does something to your entity.

JamesG
01/19/2012 12:04 PM by
JamesG

As Dino Esposito kept stressing in his book “Microsoft.NET: Architecting Applications for the Enterprise”; it Depends.

I believe the choice depends on your specific scenario. If all the controller does is coordinate a relatively simple process (e.g. CRUD) then yes, it does not do any harm to put the logic in the controller.

On the other hand, if the controller coordinating some business process, (for example receiving order information, validating the order, checking stock, check postage and tracking options, some additional business rules then finally processing the order) then the whole ordering process could be wrapped up in a service layer or business façade. The same argument holds for repositories.

If a particular database persistence scenario involves coordinating a number of SQL update queries (through ORM or parameterized SQL) and stored procedures, then yes I will gladly hide the implementation details behind a repository.

If for consistency you want all data access to go through some kind of service, then that is up to you.

If you want to use mix and match depending on your scenario that is up to you as well.

Also if the same logic is going to be reused in say a WPF or C++ application, then hiding the data access code behind a repository (or service / business façade) could be a cleaner approach.

@Ayende, Do you consider your ORM to be your data access and abstraction layer (repository)? Some folks do. Others don’t see it that way.

They see their ORM hiding behind a repository that coordinates calls to the ORM.

Neither is wrong. It depends.

Frank
01/19/2012 12:12 PM by
Frank

@Toni

Why not just mock ISession?

Matt
01/19/2012 12:37 PM by
Matt

I'm the guy who asked this question, and it was to illustrate a point more than for an answer. However, I like this post - you're describing the problem and telling us the right way to do it - thanks for that.

I actually do the same thing in my web apps, except I use structure map - it amounts to the same thing except I find unit testing a little easier (yes, some places do mandate unit tests on everything!).

@Ayende - I'm interested in your comment about transactions. I generally don't wrap my transactions up in the HTTP request scope - is there no penalty to having a BeginTransaction() at the beginning of every request in the context of NHibernate?

@Mihai - I don't want to make any assumptions about your API, but I strongly recommend not passing around IQueryables in your application. This can lead to a whole world of pain as complexity grows - especially when method chaining starts happening with shared code, and you start seeing these massive expressions being built because some developer didn't quite understand the implication of using a method that returns an IQueryable within a method that returns an IQueryable, and so on. I prefer to defining what can be retrieved and how - which admittedly is in direct contravention with what Ayende is arguing in this thread.

Craig
01/19/2012 12:41 PM by
Craig

Dave, being able to swap out NHibernate for EF or other by using Repository is a pipe dream no one has ever managed to pull off.

Mihai, I have pretty much the same architecture as you using command classes and query objects. No repositories needed.

Joseph Daigle
01/19/2012 12:51 PM by
Joseph Daigle

I think a lot of it depends on both the general architecture of the system, and certainly the team implementing it.

Where I see this pattern breaking down is with systems that have many moving parts. For instance, there might be 1 (or even more) front-end web applications. There might be a couple of back-end services that handle work from a message queue. There might be a web service API exposed for 3rd parties. And all of these "share" the same data access layer, and perhaps do many of the same things. At this point it certainly might be a good idea to abstract data access behind some internal API.

Wayne M
01/19/2012 01:11 PM by
Wayne M

I agree with this 100%. At first I was going to write how it felt "sloppy" but when I wrote out an example to show it... it wasn't sloppy at all. There isn't much difference between var customer = session.Get<Customer>(id) and var customer = customerRepository.Find(id) in fact they are the same thing. About the only thing that feels "out of place" is the using block for the session, and that's trivial.

Thank you, Ayende. I used to be a huge fan of Repositories for everything but now, not so much, not when using an ORM that does the same thing.

Matt
01/19/2012 01:29 PM by
Matt

@Wayne M - I'm not convinced. It's easy to justify with trivial examples, but what do you do when you have different ways of querying the Customers table?

For example, you have an application that also needs to fetch customers via email, or mobile number.

Do you put, in your controller

IList customers = this.Session.CreateQuery("from Customer c where c.Email = :email")
	.SetParameter("email", emailAddress)
	.List();
return customers.FirstOrDefault();

(Disclaimer: this is just an example query!!!!!)

Now, assuming this is used all over the place, do you repeat this everywhere in your application?

I would rather have this centralised in a single place so I don't need to complete this code. And if I have one central place for this query, why not have a single class for querying all kinds of Customer data instead of different patterns throughout my application?

The pattern being recommended only applies to small, non-complex architectures.

Note I'm using NHib as an example here but I'm not an NHib expert - remember other ORMS access data differently and a repository pattern really can make sense when using them.

Wayne M
01/19/2012 01:30 PM by
Wayne M

I seem to recall Ayende saying that he would use some kind of extension method to handle an example like that, but I don't 100% remember.

Matt
01/19/2012 01:31 PM by
Matt

Ugh, apologies for the terrible formatting there.

Matt
01/19/2012 01:32 PM by
Matt

That was with entity framework. NHibernate is different. You could put a static method on Customer, of course, but that has unit test implications.

Marco
01/19/2012 01:46 PM by
Marco

IMO the core of the problem stems from using one model for both writes and reads. for my write side, each use case sits behind an role interface who is responsible for fetching (including eager) and whatever else needs to be performed and simply handing back a ref to the interface. so now my application layer (client) only knows about a particular behavior abstraction and nothing else.

Alexander Nyquist
01/19/2012 01:51 PM by
Alexander Nyquist

Matt,

That's easily done using extension methods. Clean, simple and reusable.

public static IEnumerable GetAll(this ISession instance, Expression<Func<T, bool>> where) where T : class { return instance.QueryOver().Where(where).List(); }

var customers = Current.Session.GetAll(x => x.Email == "foo@bar");

Josh
01/19/2012 01:56 PM by
Josh

@Matt You can create an action filter for doing the transactions and place them where ever you need them. You can also specify the isolation level that way.

Stefan
01/19/2012 02:04 PM by
Stefan

Matt, your query could be written like this:

var customer = Session.Query() .Where(x => x.Email == email) .FirstOrDefault();

And in a view you might need to display some customer settings as well. var customer = Session.Query() .Where(x => x.Email == email) .Fetch(x => x.Settings) .FirstOrDefault();

Roland
01/19/2012 02:18 PM by
Roland

In unittests, whats different between

var filteredProducts = Testsession.Query.WhereProductsOnSale().ToList(); Asser.Equal(2, filteredProducts.Count);

var filteredProducts = ProductsRepository.GetProductsOnSale().ToList(); Asser.Equal(2, filteredProducts.Count);

Matt
01/19/2012 02:32 PM by
Matt

@Josh - nice idea - never thought of doing that but it makes perfect sense. Thanks for the tip.

@Stefan and @Alexander - I take your point and yes it does look like it covers most scenarios, the query is just an example. What about queries that require snapshot data i.e. a combination of a number of tables for a report?

Wyatt Barnett
01/19/2012 02:36 PM by
Wyatt Barnett

What about settling in the middle a bit -- I totally agree that the amount of ceremony in the "best practices examples" is absolutely overblown. But I think having direct data access inside your UI layer is a pretty bad choice as well -- even if the modern UI layer is a bit more testable and fungable than the UI layers of years past. I also think having an implied transaction around every HTTP request is perhaps overkill, especially with everything hitting the managed pipeline. Which is a really long-winded way of saying "what about our good buddy the service layer? He can handle the complex cases rather transparently, access other services and should be allowed to access data directly. We can have explicit save methods for those of us that like those things. And I don't feel like a dirty little ****** by talking to the session in MVC action methods."

John
01/19/2012 02:51 PM by
John

Talking of handling sessions what do you think of this?

http://nhforge.org/blogs/nhibernate/archive/2011/03/03/effective-nhibernate-session-management-for-web-apps.aspx

Stefan
01/19/2012 03:27 PM by
Stefan

@Matt, personally I abstract away complex scenarios when needed. Those are usually quite rare compared to the number of simple queries (in my own experience). Depends on the application/context.

Josh Schwartzberg
01/19/2012 03:57 PM by
Josh Schwartzberg

I also like adding helpers that only extract away the resource management and not further abstracting the nhibernate API like so:

public void ExecuteAndCommit(Action actionOnSession){ using(var session = _sessionFactory.OpenSession()) { using(var transaction = session.BeginTransaction()) { actionOnSession(session); } } }

and using it like:

var task = new Task(); _dbExecutor.ExecuteAndCommit((s) => s.Save(task));

Joseph Daigle
01/19/2012 04:28 PM by
Joseph Daigle

It all really just depends on how much complexity the system actually needs.

If you're just building a single web-app that talks to a single data-source, then you certainly don't need multiple levels of abstraction. You can get away with writing your data access logic against your ORM in an MVC controller because each unit of code/logic is likely to only ever exist in that one place, and thus be tested.

If you're building a larger-scale, maybe an "enterprise" level system, with multiple application-layer components, then abstracting these gritty details into a "service" layer with some well-designed API might be the pragmatic way to go.

Josh
01/19/2012 05:39 PM by
Josh

@John That is a lot of code... Also, I don't see a way to set the transactions isolation level when doing it in an HttpModule like that.

Marco
01/19/2012 05:42 PM by
Marco

so how are you recommending caching when making calls directly from the controllers? i would not think extension methods as those are "nice to have" and do not force the client using the ISession/IDbContext.

Marco
01/19/2012 05:43 PM by
Marco

i'm also talking about caching data, not the results the view displays.

Josh
01/19/2012 06:27 PM by
Josh

@John I've found using Ninject.Web.Mvc the easiest way to handle sessions. http://pastebin.com/uiwdNKY8

Stan Kazakh
01/19/2012 07:36 PM by
Stan Kazakh

Ayende had a series of posts titled "Refactoring toward frictionless & odorless code" that covered how you could wire up NHibernate transactions into your infrastructure, meaning your MVC controllers didn't have to worry about this - see http://ayende.com/blog/4803/refactoring-toward-frictionless-odorless-code-the-baseline for the first one.

Mark
01/19/2012 07:51 PM by
Mark

Ayende what are your thoughts about using S#arp Architecture and its built in repositories? I've found this framework to be really nice for larger more complex applications coded with MVC.

wrongwrong
01/19/2012 10:27 PM by
wrongwrong

Ayende is definitely wrong on this topic

Frank
01/19/2012 10:42 PM by
Frank

@Wyatt Barnett, what data access code is in the controlers? The ORM is the actual data acess layer. Your controller is telling it what to fetch.

In my experience, when people abstract the ORM behind some construct, you'll see stuff like a Search or Filter method where you pass in some abstractions to define the filter to use. So why not just use the ORM constructs (especially since most ORMs support LINQ)?

Ayende Rahien
01/20/2012 12:52 PM by
Ayende Rahien

JamesG, A controller's action is subject to SRP as well, most of the time, when I am putting a lot in the action, that is because it is doing a read, and that is the best place to put it. All of those thins that you mentioned are logic, and that happen on write actions. I have very little problem with putting them behind a service boundary.

Ayende Rahien
01/20/2012 12:53 PM by
Ayende Rahien

Matt, There is a cost for doing BeginTransaction (you have to go to the DB for it). But you can do that lazily (on first session access, frex). That is the power of abstracting away the infrastructure.

Ayende Rahien
01/20/2012 12:54 PM by
Ayende Rahien

Joseph, Why would they share the same data layer? They have drastically different needs. They might (or might not) share the same database, but that is beside the point.

Ayende Rahien
01/20/2012 12:55 PM by
Ayende Rahien

Marco, Caching is not a cross cutting concern. I am afraid that I don't understand your question

Ayende Rahien
01/20/2012 12:56 PM by
Ayende Rahien

Mark, I have done a review about S#arp (look at the reviews tag) And I have a review about S#arp Life that is supposed to be published in a few weeks

Ayende Rahien
01/20/2012 12:57 PM by
Ayende Rahien

wrongwrong, You are so articulate, you convinced me.

Matt
01/20/2012 01:31 PM by
Matt

Thanks, I had always assumed that was the case, and have wrapped transactions where I've needed them. I really like Josh's idea in the MVC world of using an attribute for that purpose.

I have to admit, with NHibernate I am seeing your point and I think a half-way approach is probably best. For complex queries that are useful in a shared context I'm still for moving it into a service layer of some sort. Update commands - service layer.

However, for simple queries, I have to concede there is no point in adding it to the repository - it serves little to no purpose. Pragmatism and applying the principle of DRY.

Dennis Doomen
01/20/2012 02:48 PM by
Dennis Doomen

I myself am slowly moving away from the more traditional repository patterns and adapting more and more aspects of CQRS. Two important articles that have been the driving force behind that are these:

"Meanwhile… on the command side of my architecture" http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91

"Meanwhile… on the query side of my architecture" http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92

We've applied both in a large enterprise-class system and they've proven to make our life a lot easier. We've applied TDD all the way and usually use SqlLIte to make sure our optimized queries still work when using NH's new QueryOver API.

Comments have been closed on this topic.