Simple Abstractions

Most of us agree by now that the single responsibility principle is a good idea. Small, composable parts are easy to read, write, test and maintain.

This introduces another issue though, wiring these components together. IOC containers do a great job of constructing our object graphs, but our components still have to specify their dependencies.

Writing essentially the same constructor code over and over gets old fast. Typically projects will resort to some combination of property injection or the service locator pattern to avoid this. Neither of these options is particularly helpful from a code readability or unit testing point of view.

So how do we get the benefits of the single responsibility principle without the drawbacks? First we have to take a step back and look at our interactions. Lets look at the interactions of a controller in a typical MVC application.

What does a controller do?

A controller is responsible for coordinating the interaction between the user interface and the rest of the application.

The user interface is how we interact with the users themselves, usually be returning some HTML via a view or some json data. Often we will also return some type of message to the user or redirect the user to another page.

The rest of the application could be just about anything. Typically there will be some type of database to read/write from, some sort of logging, some sort of message queue, etc. The specifics will vary from application to application.

So let's see what happens when we model these interactions with the IUser and IApplication interfaces.

IUser

Here is what an IUser interface may look like:

public interface IUser {
  public string UserName  { get; }
  public int Id { get; set; }
  public string[] Roles { get; set; }

  public void Notify(string message);
  public void Warn(string message);
  public T Session<T>(string index);
  public ActionResult Redirect();
  public ActionResult Redirect(string location);
  public ActionResult View();
  public ActionResult View(object model);
}

The first few properties are some general information about the user that the controller will need either directly or indirectly. Other components may need this information, but as the coordinator it should be the controller passing it along.

Next there is the Notify and Warn methods, this is how the controller can communicate with the user. A success or failure method would be sent to the user via these methods. The actual implementation would store this somewhere and make it available to the view.

Session is our temporary storage area. Because sessions are per user, the IUser interface is the natural place to put it.

The rest of the methods deal with returning action results. Depending on the design you may or may need to return a result from an MVC action.

IApplication

An IApplication interface could look something like this:

public interface IApplication {
  public void LogMessage(string message);
  public void LogError(string message);
  public void PublishMessage(object message);
  public Query<T> Query<T>();
  public Map<T, U>(T mapFrom);
}

The log methods are self explanatory. PublishMessage will publish the message to a message queue. Query will delegate to the CQRS sub system and Map will delegate to automapper.

The actual specifics will vary tremendously from project to project.

Single Responsibility Principle

It is natural to assume that any implementation of these interfaces will violate the single responsibility principle, that they would be handling many unrelated tasks.

This is where the facade pattern comes in. An implementation of these interfaces should be a facade only, They should contain no real logic, simply pass arguments to a real implementation.

They only responsibility of these implementations is to delegate:

public class System : IApplication {
  private readonly ILog Log;
  private readonly IBus Bus;
  private readonly ISession Session;

  public System(ILog log, IBus bus, ISession session) {
    Log = log;
    Bus = bus;
    Session = Session;
  }

  public void LogMessage(string message) {
    Log.LogMessage(message);
  }

  /* ... */

  public Query<T> Query<T>() {
    return Session.Query<T>();
  }
}

So now we have a system with many small, testable components. Our controllers only have two dependencies to worry about. And finally, our IOC container can construct our object graph purely from constructors.

We've got all the benefits of SRP and IOC, with none of the downsides.