BHW Group Blog

ASP.Net MVC Principles - Part 3

Overview

In parts one and two of our four part series on ASP.Net MVC Principles we discussed the object oriented design concepts behind our application template.  We also discussed Dependency Injection and how our application template is divided into three layers, Data Access Layer, Services Layer, and Presentation Layer. We described the Data Access Layer in the last article.  In this article we will describe our services and presentation layers.

services and data access layer

Services Layer

The services layer is dedicated to business logic and integrates to the repositories from the data access layer.  This makes the services layer responsible for business logic as well as populating view models and updating edit models.  By encapsulating our data access layer we also remove the services dependency on the specific concrete class for accessing the data.  This separation allows us to execute the services layer in production against a SQL database while pointing our test environment to Mocks or static Fixtures for automated unit testing.

Another advantage of encapsulating the services layer is that we can share the business logic assemblies between different applications.  Perhaps our client’s first application is a web app, but later they create a mobile strategy and need to provide parts of the web functionality via remote calls from a mobile app.  The services layer separation physically keeps our reusable business logic out of the presentation layer, so we can simply construct a JSON WEB API layer in front of our tested, proven services layer to support our new mobile apps.  Our previous investment in the logic, testing, maintenance, and upgrades in our services layer results in a huge savings when we reuse these features for new callers and clients.  Many times you don't know in advance that you might need reuse, so it's best to start loosely coupling these systems from the beginning.

We previously mentioned that in some cases we return IQueryable<> from the DAL into the services layer.  This is a controversial topic with experts weighing in on both sides.  If you chose to return IQueryable as well then the service layer is where you can use query composition to continue layering on filters on the IQueryable<> result - deferring execution until the results are actually needed.  Regardless of a deferred execution model or when working with enumerated objects, when we do pull results back from the DAL, they are in the shape of our domain POCO’s defined in the DAL.  These domain POCO’s aren’t suitable to be used directly in the presentation layer for a number of reasons.  First, the domain entities follow a normalized database design that often isn’t the most useful way to display data to a user.  Second, MVC has great model binding capabilities, but again the shape of the domain entities can make model binding a real challenge.  Often it works for simple objects and fails miserably for anything more complex.  Third, navigating domain model relationships is prone to NullReferenceExceptions when you dereference a property of a related entity for display in the view, think Person.Address.City where address is a 0 or 1 relationship.  We don’t like writing code in our view to protect against these scenarios, it’s a lot easier to simply write PersonView.City and let the view engine represent the null as a blank when rendered.  There are many other reasons for the separation between view/edit models and the domain models - different validation rules depending on the actor or page, and hiding sensitive domain data from serialization into JSON structures that a malicious user could inspect from AJAX calls.

FubuMVC encourages a one-to-one relationship of Views to ViewModels.  While we don’t immediately take our projects to this level, we usually end up very close.  So you may be thinking, “wow, that’s a lot of copy and assignment code to write”.  You’ve got domain objects like Person coming back from the DAL and you have to selectively copy 5 to 10 properties into a PersonView model for display.  Later you may copy the same properties into a PersonAddressView model, etc.  If we add one property to the Person object and need it on several views - that’s a lot of assignment code to maintain!

Automapper to the rescue.  Automapper uses a convention over configuration philosophy to copy properties from our domain objects into our view or edit models, usually with a single line of code.  Automapper has multiple conventions that match up property names or hierarchies of property names, like PersonAddressCity which would populate from Person.Address.City.  It’s an immense time saver and a tool we couldn’t live without.

Presentation Layer

Our presentation layer is where our controllers, views, routes, and supporting assets live.  Today we often use AngularJS or other Javascript MVC frameworks instead of relying on complete server-side rendering.  As a result, our presentation layer has a lot of variability depending on if we’re doing something like a JSON Web API for AngularJS and Mobile apps, or if we’re doing server-side rendering via Razor for a desktop or mobile website.

Regardless of the client, one rule of thumb we try to follow is “Fat Model / Skinny Controller”.  Controllers should be very lightweight, simply making service layer calls and serializing their output for the end client.  When you read other commentary you’ll notice a consensus that controllers are hard to test.  By keeping controllers “skinny” we limit the amount of code in our solution that is difficult to test with automated unit tests.  We do worry that “Fat Model” encourages the wrong idea though.  No class should really be so long to be considered  “fat”, so in many cases helper classes or other patterns can be used to keep model classes focused, like SOLID principles advocate.

Some notable principles we follow in our presentation layer:

  • Strongly Typed Views - ASP.Net provides the option to create a strongly typed view, a view that is based on a view model or edit model.  There are many advantages to using this feature like Intellisense and type safety of view code.  FubuMVC advocates a “one model in, one model out” approach to controller actions and views.  We follow this practice because our views typically place a lot of constraints on our models and those constraints can vary from view to view.  It’s often tempting to reuse view models from other controller actions, and pragmatism does win us over on occasion.
  • Minimize the use of the ViewData collection - for many of the same reasons that strongly-typed views are great, view data is not.  First, ViewData is not typed, meaning that run-time errors can sneak up from unexpected data.  Second, the code that populates view data is sometimes difficult to reuse, and doesn’t DRY up our controllers when it appears in many different controller actions.  Finally, ViewData doesn’t refactor the way models do, so you’re more inclined to have errors (or obsolete code) when making changes to the system later.
  • RenderAction - Obviously it’s difficult to build an MVC system without ViewData, so in the cases where we would typically use ViewData we try to use RenderAction instead.  RenderAction does what it says, it renders a specific action of a controller into an area of our view.  Using RenderAction we are able to put ViewData-backed controls or widgets into our view via the advantages of strongly-typed views.  Another option is using JSON requests to lookup actions.  In this scenario we typically implement with AngularJS or another Javascript MVC, but you could write AJAX and jQuery too.
  • FUBU MVC HtmlTags - We recently became aware of these fantastic HTML builders from the FUBU MVC project.  HtmlTags can compose HTML expressions in your Razor template the same way that Linq expressions or jQuery expressions can be chained.  It frees us from writing expressions with long lists of tags in anonymous types and curly braces.  Instead of writing this:

<% Html.TextBoxFor( m => m.Username, new{ id = "user", @class = "col-md-2", placeholder="Username", required = "" } ) %>

try this

<% HtmlTags.TextBoxFor( m => m.Username).Id("user").Class("col-md-2").Placeholder("Username").Required() %>

These topics just scratch the surface of some of our template recommendations for the presentation layer.  We plan to have a future article that goes into more detail about how we handle other common scenarios in ASP.Net MVC like “magic strings”, model editors, etc.

Conclusion

In our next and final article we will cover our template and the business value we realize by employing our template on greenfield projects.

Do you need an expert in web development? With a team of web development specialists covering a wide range of skill sets and backgrounds, The BHW Group is prepared to help your company make the transformations needed to remain competitive in today’s high-tech marketplace.

You may also like

Categories:
Brett is a customer-focused executive who works closely with his team members and often takes a hands-on role in project execution. He blends strong business skills with deep technology expertise, allowing him to bridge the often insurmountable gap between business and IT. By mastering both domains, Brett can quickly conceive and operationalize new solutions that are technically elegant and commercially successful.