I do my queries in the ASP.NET MVC controller with RavenDB.

ASP.NET MVC,RavenDB 28 November 2011 | 8 Comments

This blog post has been brewing for a few weeks now, mainly since I had the eureka moment with RavenDB about how I should structure my ASP.NET MVC application’s data access. Mainly, I shouldn’t.

Probably like many coming to RavenDB from the heavyweight ORM world such as Entity Framework, you feel the need to continue the “best practices” you were painfully forced to use, such as Repositories, Unit of Work pattern and a service layer, mainly in my case by peer pressure and a short stint in a “real enterprise” software environment.

With RavenDB, I’ve learnt to throw away the layers of (useless) abstractions. Simply as they get in the way and often are abstracting an abstraction, RavenDB provides the unit of work pattern built in!

My journey started with the architecture of my ASP.NET MVC application using a service layer that the controller called, such as:

[HttpGet]
public ActionResult Edit(string name)
{
	var model = _fooService.GetSingleFoo(name);

	var viewModel = Mapper.Map<FooViewModel>(model);

	return View(viewModel);
}

This worked quite well till I encountered a scenario where I needed to pull several different parts of data together into one view, a detailed view. This resulted in my controller having 4 dependencies, it was added complexity which just hid my original intent of the code and meant I had a real problem taking advantage of RavenDB’s real strengths.

[HttpGet]
public ActionResult View(int fooId, int barId, int blahId, int baseId)
{
	var fooModel = _fooService.GetSingleFoo(fooId);

	var barModel = _barService.GetSingleBar(barId);
	
	var blahModel = _blahService.GetSingleBlah(blahId);
	
	var baseModel = _baseService.GetSingleBase(baseId);
	
	return View(new FBBViewModel { foo = fooModel, bar = barModel, blah = blahModel, base = baseModel });
}

“Wait!” You yell at me, “You could hide all that in some kind of view model assembler class so your controllers are clean!” I did think about this but, you’re just moving the problem down a layer, and adding another layer making it even more complex and painful.

Even with the service layer approach, to figure out what’s going on I have to navigate through 4 classes and their methods. Plus how the heck do I easily implement RavenDB’s lazily feature with this? I guess I could add an overload of GetSingleFoo that returns a Lazy?

Or. I could remove those layers of abstraction and just do my queries in the controller and take advantage of Lazily (plus like my previous post, aggressive caching and the many more awesome things)?

[HttpGet]
public ActionResult View(int fooId, int barId, int blahId, int baseId)
{
	var fooModel = Session.Advanced.Lazily.Load<Foo>(fooId);

	var barModel = Session.Advanced.Lazily.Load<Bar>(barId);
	
	var blahModel = Session.Advanced.Lazily.Load<Blah>(blahId);
	
	var baseModel = Session.Advanced.Lazily.Load<Base>(baseId);

	Session.Advanced.Eagerly.ExecuteAllPendingLazyOperations();

	return View(new FBBBViewModel { foo = fooModel.Value, bar = barModel.Value, blah = blahModel.Value, base = baseModel.Value });
}

RavenDB will cleverly bundle these Load operations up and execute them all at once in one request to the server. Oh, also it does them all in parallel, so its super quick.

The next trap of clean code I fell into was the “that’s doing 4 things, it needs splitting up”. I posted to the user group asking how do I improve the controller’s design to separate those 4 query calls. I think Ayende’s response summed up the trap I was falling into:

Phil,
You seem to have replaced the notion of a service layer (and the N layered
repositories) with a controller layer.

If you need the detailed view, why not generate all of it in a single
action?
If you have queries that are repeated, you might want to create an
extension method for that, but that is about as far as I got with
abstracting my data access.

So I did. And I’m happy with it personally. I obviously unit test it and I have no problem with it. RavenDB has an in-memory option that I use for unit testing. Using abstractions and in memory lists in the past, I’ve found sometimes database problems slip through, hence why I took this approach.

This will anger quite a few people. I certain know it breaks a few SOLID rules, but, I’ve become sick of pressing the F12 button in Visual Studio to figure out what’s going on and not being able to take (easy) advantage of the platform I’m using due to the use of n+1 layers of abstraction.

A few blogs by Ayende of interest on the subject:
The wages of sin: Proper and improper usage of abstracting an OR/M
The wages of sin: Over architecture in the real world
Architecting in the pit of doom: The evils of the repository abstraction layer

8 Responses on “I do my queries in the ASP.NET MVC controller with RavenDB.”

  1. Ramon says:

    What is the use of lazy loading here, if the next thing you do is fetching the data?

  2. phil says:

    Lazy loading isn’t like EF, to quote myself:

    RavenDB will cleverly bundle these Load operations up and execute them all at once in one request to the server. Oh, also it does them all in parallel, so its super quick.

  3. This is probably more a RavenDB question but a line like

    Session.Advanced.Lazily.Load(barId).Lazily();

    seems redundant API-wise. What does the first “lazily” do, what the second?

  4. phil says:

    @Frank

    You are right. Its a mistake in my code sample as I translated it from Query to Load for people to better understand. (Query is as it sounds a query, but Load is when you know the document’s ID or multiple document’s ID’s)

    Session.Advanced.Lazily.Load(id);

    Originally it was a query like:

    Session.Query().Where(x => x.Type == “bar”).Lazily();

  5. Bob says:

    Testing against an in memory ravendb database is NOT unit testing, this is integration testing :).

  6. Nick says:

    It’s all about trade-offs, and if those queries conveyed intent about your domain – then you’ve just lost that expresiveness. I suspect not however.

    But passing your domain entities to the view – I’ve always found that to be a problem – because the view affects the design of the domain model. I would map them to properties on the model – but maybe you are already doing that.

    If you are sick and tired of “best practices” then look up CQRS and view caches – gets even simpler.

  7. Andrew says:

    Bob,

    That’s just symantics, hitting a DB to properly test has been done in other languages for years. For some reason .NET gets so hung up on the minutiae, it’s like they can’t see the forest for the trees.

  8. phil says:

    @Andrew

    I’ve never understood why not use the database? You’re testing against the actual thing. Abstractions can’t throw errors specific to your database.

    @Nick

    I’ve never bothered reading about CQRS but the website’s “write” operations are done in a different web project so I guess its separated already?

Leave a Reply