The required infrastructure frees you from infrastructure decisions

time to read 5 min | 871 words

One of the things that I try to do whenever I design a system is to have just enough infrastructure in place to make sure that I don’t have to make any infrastructure decisions.

What does this means? It means that the code that actually does the interesting things, the code that actually provide a business value, isn’t really aware of the context that it is running on. It can make assumptions about the state of the world in which it runs.

For example, let us take this controller, it inherits from RavenController, and it assumes that when it runs, it will have a documentSession instance primed & ready for it:

public ActionResult Validate(Guid key)
{
  var license = documentSession.Query<License>()
    .Customize(x=>x.Include<License>(y=>y.OrderId))
    .Where(x=>x.Key == key)
    .FirstOrDefault();

  if (license == null)
    return HttpNotFound();

  var orderMessage = documentSession.Load<OrderMessage>(license.OrderId);

  switch (orderMessage.OrderType)
  {
    case "Perpetual":
      return Json(new {License = "Valid"});
  }

  return HttpNotFound();
}

What is important about that is that it isn’t actively doing something, it just assumes that it is there.

Why is that important? It is important because instead of going ahead and creating the session, we assume it is there, we are unaware of how it got there, who did it, etc. If we wanted to execute this code in a unit test, all we would need to do is to plug a session in, and then execute the code.

But that is just part of this. Let us look at a more complex scenario, the processing of an order:

public class ProcessOrderMessageTask : BackgroundTask
{
  public string OrderId { get; set; }

  public override void Execute()
  {
    var orderMessage = documentSession.Load<OrderMessage>(OrderId);

    var license = new License
    {
      Key = Guid.NewGuid(),
      OrderId = orderMessage.Id
    };
    documentSession.Store(license);

    TaskExecuter.ExecuteLater(new SendEmailTask
    {
      From = "orders@tekpub.com",
      To = orderMessage.Email,
      Subject = "Congrats on your new tekpub thingie",
      Template = "NewOrder",
      ViewContext = new
      {
        orderMessage.OrderId,
        orderMessage.Name,
        orderMessage.Amount,
        LiceseKey = license.Key
      }
    });

    orderMessage.Handled = true;
  }


  public override string ToString()
  {
    return string.Format("OrderId: {0}", OrderId);
  }
}

There are several things that are happening here:

  • Again, we are assuming that when we run, we are actually going to run with the documentSession set to a valid value.
  • Note that we have the notion of TaskExecuter.ExecuteLater.

That is actually quite an important aspect of infrastructure ignorance.

ExecuteLater is just that, a promise to execute something later, it doesn’t specify how or when. In fact, it takes this a bit farther than that. Only if the current operation will complete successfully will this task run. So if we failed to commit the transaction, the SendemailTask won’t get executed. Those sort of details can only happen when we let go of trying to control everything and let the infrastructure support us.

During the Full Throttle episode in which this code was written, we moved the task execution from a background thread to a different process, and nothing in the code had to change, because the only thing that we had to do is to setup the environment that the code expected.

I really like this sort of behavior, because it frees the code from all the nasty infrastructure concerns, at the same time as it gives you an incredibly powerful platform to work on.

And just for fun, this result in a system that you can play with with great ease, you don’t have to worry about your choice. You can modify them at any time, because you don’t really care how those things are done.