Dealing with transactions

In the last post on NServiceBus, I got quite a few comments that one way to fix the problem of dealing with non-transactional operations that must happen if some transaction succeeds is to simply move the non-transactional operation after the transactional one, so that I know that the transaction succeeds. If we delay the sending of an email until after the transaction succeeds, our picture now looks like this:

image

The steps are slightly altered so that the Commit happens before Send. If step 2 fails, step 3 never happens. I see a couple of problems with this approach, namely in that it assumes:

  1. I have explicit control over when transactions happen
  2. I want explicit control over when transactions happen

If #1 is true, I have to wonder if #2 is also desirable. In most cases I run into, I don’t want explicit control over transactions. I don’t want to think about them, or mess with them, or deal with them. I want these to just happen without me having to do any work.

I instead like the idea of a unit of work, or at the very least, a concept of required infrastructure for transaction management. Regardless of the host environment I’m working with, WCF, WPF, ASP.NET, NServiceBus etc., my day to day development takes on a picture like this:

image

Day to day, I’m living in the green section. I don’t want to “remember” to create transactions, deal with a pattern of saving, committing, rolling back etc. This should just be taken care of for me, through required infrastructure. One example is in Ayende’s example of the RavenController in MVC:

using System;
using System.Web.Mvc;
using Raven.Client;
using TekPub.Profiler.BackOffice.Tasks;

namespace TekPub.Profiler.BackOffice.Controllers
{
	public class RavenController : Controller
	{
		protected IDocumentSession documentSession;

		protected override void OnActionExecuting(ActionExecutingContext filterContext)
		{
			documentSession = MvcApplication.DocumentStore.OpenSession();
		}

		protected override void OnActionExecuted(ActionExecutedContext filterContext)
		{
			try
			{
				using(documentSession)
				{
					if(filterContext.Exception == null)
					{
						documentSession.SaveChanges();
						TaskExecuter.StartExecuting();
					}
				}
			}
			finally
			{
				TaskExecuter.Discard();
			}
		}
	}
}

On every request, a Raven session is opened, and everything saved at the end of the request. All developers have to do to make sure that transactions are used properly is simply inherit from this controller.

With every application framework I look at, one of the first items I put in is the concept of implicit contextual transactions. The scope of what code I’m working with naturally fits inside a transaction, so it’s not something I want to have to worry about.

If I have to remember to call Commit on a transaction every time I introduce new data manipulation code, that’s just something I’m going to forget to do. Instead, transactions and unit of work management should wrap around my normal application code, without me needing to do anything to manage it.

In the next post, I’ll look at various alternatives to messaging for doing something non-transactional like sending emails (or calling web services), and examine the benefits and drawbacks of each approach.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Architecture. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.Marisic.Net dotnetchris

    Is there a reason you have a RavenController instead of RavenTransactionActionFilterAttribute?

    • Anonymous

      Keep in mind that it’s Ayende’s code :) But supposed we did. That would mean that I would need to put that filter attribute on my actions (which I would forget). Let’s put it on all of our controllers (which I would also forget). At that point, you can just put it on the controller, which implements all of the filter interfaces. Also, the session is just on a protected field in the base controller, which would make it hard to get to.
      If you did have DI and all that fun-ness, I would go the action filter route, however.

      • http://profiles.google.com/smckoy Sam McKoy

        You would forget to put a filter on a controller but wouldn’t forget to set it’s baseclass?  

        Surely if you go to the effort to abstract your unit of work this much you’re probably the type of person that already uses DI/can make global filters.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #991

  • Radu Vunvulea

    What I don’t like at this solution is that the controller have knowledge about the persistence layer and about the transaction. On the UI layer, we control how data store works. I don’t think that UI should have any knowledge about the data store and transactions.

    • Anonymous

      So push it down a layer then. Create an action filter or http module.

      • Philliep.Menelik@yahoo.com

        We can write a http module for it, but the problem is not solution.
        The UI level knows about the storage. I don’t have a better solution for this problem now, but something is not okay.

        • Anonymous

          I’m not sure the UI knows about storage necessarily. You could wrap it up in a unit of work class that hides the actual implementation.

          What pain does having this in the UI layer cause?

  • Pingback: Distributed Weekly 131 — Scott Banwart's Blog

  • Pingback: Session-per-request with Nancy « Look on my works, ye Mighty, and despair!