Tuesday, May 22, 2012

Structuring an ASP.Net MVC App

Your way is WRONG!

I'm kidding, I'm kidding... haha. Everyone has their own way to do this, and I'm no exception I guess. After 5-6 MVC projects, I feel like I've come to a formula that really works for me and I wanted to share. I've decided to do a series on my personal rendition of structuring an ASP.Net MVC application. I'm going to focus on the current state of things, which is MVC 3 for now. I'll do MVC 4 again maybe when it's out of beta. It has a lot of nice features that may or may not replace what I'm going to talk about here, so this content may be a bit dated in a few months. Such is tech.

Project Structure

The first thing I like to set up is a project structure with the following projects. For sake of  this series, I'm going to use a parent namespace "NS". The projects I add right off are as follows:

  • NS.sln - the solution file.
    • NS.dll - A class library to house global code and helpers, I always have these, so I feel okay adding it right off.
    • NS.Web.csproj - Our MVC app itself.
    • NS.EF.dll - A class library for our actual data access, for the sake of this series, I'll be using EntityFramework, but it could be anything, really.
    • NS.Data.dll - A class library that will house our model and our data access methods that build them. More on this later.

Project Dependencies

Here's where, IMO, a lot of developers drop the ball. I really don't want my web project to be dependent on Entity Framework, or SQL, or MySQL, or MongoDB, or anything like that. I want a level of abstraction, especially when that sort of abstraction comes so easily. Do to this I set up my project dependencies accordingly. Below is a diagram of how my project reference each other.



Wiring Everything Up

The first thing I do is go into NS.EF and generate my entities using EntityFramework. If you're using something else, I think it's a good idea to flush out your actual database access or ORM or whatever right now. It's clearly a critical piece.

Once that is done it's time to move forward to the UI and work out a Controller Action. I have a requirement to create a page that writes out an unordered list of "Foos" from my data. So I'm going to create a FooController.cs file in my Controllers directory, then I'm going to create an Action on it called "List". I'm also going to create a view file in /Views/Foo called List.cshtml.

Beyond that we need to set up our data access for the controller, and that means hopping back to our NS.Data project and adding and IFooService interface, then implementing it. The important thing here is we don't want any consuming code to be able to create an instance of the IFooService implementation directly, so for our implementation, which I'll call FooService, I'm going to leave the modifier off of it, making it internal, whereas IFooService will obviously be public. So how does the web project create an instance of IFooService if it can't see the implementation? With a little static class I'll call ServiceFactory. ServiceFactory will have a static method on it (also called FooService) that creates a FooService (the implementation) and returns IFooService.

Now we head back to our controller for a little dependency injection: I'm gion to add a protected readonly field to stick an IFooService in and call it IFooService. Then I'm going to add two contuctors, one that accepts my dependency, or IFooService, and other that is parameter-less that creates and passes to the other constructor an IFooService via the ServiceFactory.FooService() method.

Now I'm going to set up a Repository pattern for our actual data access via EF. This is done so we can use dependency injection to test our building of the models in FooService. So to do this, I'm going to add a IFooRepository, a FooRepository (the implementation) and a RepositoryFactory to the NS.EF project in the same manner I created and implemented the Services and ServiceFactory. This repository will be to do all of the accessing of EntityFramework. This way, EF is abstracted out enough we can test without having to mock anything more than just a repository implementation.

I want to state this here: I don't like code generated repository patterns. Sure they might save you a little time up front, but they hardly ever do everything you need them to do, they generate a lot of code you'll never use, and IMO, they don't add any value, if anything they just add one more level of complexity to what is really a simple problem: Getting and setting data in some datasource. The underlying ORM is more than enough, I think.

Once that is done, I'm able to go into my Action method and call my GetFooList() method, which returns a model (housed in NS.Data as well), then return an ActionResult with the proper view and the model we just got.

Below you'll see some pseudo-code to help you get the basic idea.

C# - FooController.cs

public class FooController : Controller
{
   /*
    * our service from NS.Data
    * */
   protected readonly IFooService FooService;

   /* 
    * A little ctor magic for dependency injection. 
    * ServiceFactory is from the NS.Data namespace and 
    * provides static methods for instantiating a new service.
    * */
   public FooController() : this(ServiceFactory.FooService())
   {
   }
   public FooController(IFooService fooService) 
   {
      FooService = fooService;
   }

   /*
    * Here we have our Action method.
    * */
   public ActionResult List() 
   {
      var model = FooService.GetFooList();
      return View("List", model);
   }
}

C# - IFooService.cs (NS.Data)

public interface IFooService
{
   FooListModel GetFooList();
}

C# - FooService.cs (NS.Data)

/* Our actual implementation is going to be left internal
 * (NOT PRIVATE) classes with no specifier are internal.
 * Because we don't want outside code creating instances of 
 * our implementations directly. */
class FooService : IFooService
{
   // our repository dependency.
   protected readonly IFooRepository FooRepository;

   // hot dependency injection.
   public FooService() : this(RepositoryFactory.Foo())
   {
   }
   public FooService(IFooRepository fooRepository)
   {
       FooRepository = fooRepository;
   }
   // do the work of getting the data and building the model.
   public FooListModel GetFooList()
   {
      var model = new FooListModel();
      // get our entities from the Repository.
      var fooEntities = FooRepository.GetFoos();
      /* ... build the model out here from the entities ... */
      return model;
   }
}

C# - ServiceFactory.cs (NS.Data)

public static class ServiceFactory
{   
   /* This is where we're going to get our instance of IFooService
    * from for our Controllers. */
   public static IFooService FooService()
   {
      return new FooService();
   }
   /* this one allows dependency injection */
   public static IFooService FooService(IFooRespository fooRepos)
   {
      return new FooService(fooRepos);
   }
]

C# - IFooRepository.cs (NS.EF)

public interface IFooRepository
{
   FooEntity[] GetFoos();
}

C# - FooRepository.cs (NS.EF)

class FooRepository: IFooRepository
{
   /* In here we're going to do the work of getting 
    * some entities from Entity Framework. I like to
    * return arrays or lists or something that forces me
    * to enumerate my queryable to make sure I don't abuse
    * EntityFramework. */
   public FooEntity[] GetFoos()
   {
        using(var dbcontext = new WhateverEFContext())
        {
            /* do whatever you need to do to get your IQueryable */
            return someQueryable.ToArray(); //make sure you only enumerate ONCE!
        }
   }
}


C# - RepositoryFactory.cs (NS.EF)

public static class RepositoryFactory 
{
   public static IFooRepository Foo()
   {
      return new FooRepository();
   }
}

So this is probably all about as clear as mud now, huh? There is just a LOT to go over. I'd recommend googling some articles on why we should use dependency injection, or why it's a good idea to separate your database access code from your Controller Actions. I'd rather not get into that myself, as it's a lengthy discussion and frankly, most of my friends that read this blog are already all over those concepts, in most cases more so than I am.

When I'm all finished with this series, perhaps I'll put together a sample project and host it somewhere for people to pull down and use. There are still several things that I do in my MVC apps I want to touch on, such as Script and Style location and placement, Bundling and Minification with Cassette, other nice to haves like Logging and Profiling, etc. Probably nothing new to everyone, but I'm hoping someone that reads this finds it helpful at some point.

EDIT: I've changed a few things about the way I've named my namespacing and structured things. I had forgotten to implement a repository pattern on the data access side of things, and that caused the Service implementations to be a bit hard to test. I'm going to try to edit this post to rectify that.

No comments:

Post a Comment

This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)