Published by Marc on 29. Aug 2014 14:06:01
------------------------------------------------------------------------

After some initial skepticism regarding Areas, I now use them more and more when
building new Web-Applications using ASP.Net MVC. Therefore, I decided to cover
some of my thoughts and experiences in a blog post so others may get some
inspiration out of it.

Before we start, here's a link to a general introduction to the area feature of
MVC. Check out "this article"
 if you are not
yet familiar with Areas.

Furthermore, this topic is based on MVC 5 and C# 4 but may also apply to older
versions too as Areas are not really a new thing and were first introduced with
MVC 2.

[Introduction]

Areas are intended to structure an MVC web application. Let's say you're
building a line-of-business application. You may want to separate your modules
on one hand from each other and on the other hand from central pieces of your
web application like layouts, HTML helpers, etc.

Therefore an area should be pretty much self-contained and should not have too
much interaction with other areas -- as little as possible -- as otherwise the
dependencies between the modules and their implementation grows more and more,
undercutting separation, and resulting in less maintainability.

How one draw the borders of each area depends on the need of the application and
company. For example, modules can be separated by functionality or by
developer/developer-team. In our line-of-business application, we may have an
area for "Customer Management", one for "Order Entry", one for "Bookkeeping" and
one for "E-Banking" as they have largely separate functionality and will likely
be built by different developers or even teams.

The nice thing about areas -- besides modularization/organization of the app --
is that they are built in MVC and therefore are supported by most tools like
Visual Studio, R# and most libraries. On the negative side, I can count the lack
of good support in the standard HtmlHelpers as one need to specify the area as a
routing-object property and use the name of the area as a string. There is no
dedicated parameter for the area. But to put that downside into perspective,
this is only needed when making an URL to another area than the current one.

[Application modularization using Areas]

In my point of view, modularization using areas has two major advantages. The
first one is the separation from other parts of the application and the second
is the fact that area-related files are closer together in the Solution
Explorer.

The separation -- apart from the usual separation advantages -- is helpful for
reviews as the reviewer can easily see what has changed within the area and what
changed on the core level of the application and therefore needs an even-closer
look. Another point of the separation is that for larger applications and teams
it results in fewer merge conflicts when pushing to the central code repository
as each team has its own playground. Last but not least, its nice for me as an
application-developer because I know that when I make changes to my area only I
will not break other developers' work.

As I am someone who uses the Solution Explorer a lot, I like the fact that with
areas I normally have to scroll and search less and have a good overview of the
folder- and file-tree of the feature-set I am currently working on. This happens
because I moved all area-related stuff into the area itself and leave the
general libraries, layouts, base-classes and helpers outside. This results in a
less cluttered folder-tree for my areas, where I normally spend the majority of
my time developing new features.

[Tips and tricks]

  * Move all files related to the area into the area itself including
    style-sheets (CSS, LESS, SASS) and client-side scripts (Javascript,
    TypeScript).
  * Configure bundles for your area or even for single pages within your area in
    the area itself. I do this in an enhanced area-registration file.
  * Enhance the default area registration to configure more aspects of your
    area.When generating links in global views/layouts use add the area="" routing
  attribute so the URL always points to the central stuff instead being
  area-relative.

  For example: if your application uses '@Html.ActionLink()' in your global
  _layout.cshtml, use:


      @Html.ActionLink("Go to home", "Index", "Home", new { area = "" });


[Area Registration / Start Up]

And here is a sample of one of my application's area registrations:


public class BookkeepingAreaRegistration : AreaRegistration
{
  public override string AreaName
  {
    get { return "Bookkeeping"; }
  }

  public override void RegisterArea(AreaRegistrationContext context)
  {
    RegisterRoutes(context);
    RegisterBundles(BundleTable.Bundles);
  }

  private void RegisterRoutes(AreaRegistrationContext context)
  {
    if (context == null) { throw new ArgumentNullException("context"); }

    context.MapRoute(
      "Bookkeeping_default",
      "Bookkeeping/{controller}/{action}/{id}",
      new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
  }

  private void RegisterBundles(BundleCollection bundles)
    {
      if (bundles == null) { throw new ArgumentNullException("bundles"); }

      // Bookings Bundles
      bundles.Add(new ScriptBundle("~/bundles/bookkeeping/booking")
          .Include("~/Areas/bookkeeping/Scripts/booking.js"
      ));

      bundles.Add(new StyleBundle("~/bookkeeping/css/booking")
          .Include("~/Areas/bookkeeping/Content/booking.css"));

      // Account Overview Bundle
      ...
  }
}

As you can see in this example, I enhanced the area registration a little so
area-specific bundles are registered in the area-registration too. I try to
place all areas-specific start-up code in here.

[Folder Structure]

As I wrote in one of the tips, I strongly recommend storing all area-related
files within the area's folder. This includes style-sheets, client-side scripts
(JS, TypeScript), content, controller, views, view-models, view-model builders,
HTML Helpers, etc. My goal is to make an area self-contained so all work can be
done within the area's folder.

So the folder structure of my MVC Apps look something like this:

  * App_Start
  * Areas
    * Content
    * Controllers
    * Core
    * Models
      * Builders
    * Scripts
    * Views
  * bin
  * Content
  * Controllers
  * HtmlHelpers
  * Models
    * Builders
  * Scripts
  * Views

As you can see, each area is something like a mini-application within the main
application.

[Add-ons using Areas deployed as NuGet packages ]

Beside structuring an entire MVC Application, another nice usage is for hosting
"add-on's" in their own area.

For example, lately I wrote a web-user-interface for the database-schema
migration of our meta-data framework Quino. Instead of pushing it all in
old-school web-resources and do binary deployment of them, I built it as an
area. This area I've packed into a NuGet package (.nupkg) and published it to
our local NuGet repo.

Applications which want to use the web-based schema-migration UI can just
install that package using the NuGet UI or console. The package will then add
the area with all the sources I wrote and because the area-registration is
called by MVC automatically, it's ready to go without any manual action
required. If I publish an update to the NuGet package applications can get these
as usual with NuGet. A nice side-effect of this deployment is that the web
application contains all the sources so developers can have a look at it if they
like. It doesn't just include some binary files. Another nice thing is that the
add-on can define its own bundles which get hosted the same way as the MVC app
does its own bundles. No fancy web-resources and custom bundling and
minification is needed.

To keep conflicts to a minimum with such add-on areas, the name should be unique
and the area should be self-contained as written above.