ASP.Net MVC Principles – Part 2
Overview
In our first article we explained the object oriented design patterns behind our ASP.NET MVC Principles. Here we explain how our MVC projects are divided into different abstraction layers and how those layers interact via Dependency Injection.
Dependency Injection
Earlier we mentioned that Dependency Injection (DI) is the core of our app template. DI goes hand-in-hand with Inversion of Control (IoC). If you are familiar with dependency injection you know that it is typically used in a factory pattern to create objects and their dependencies. The IoC pattern is also used to map interfaces to concrete types, allowing us to use interfaces as parameters to our service layer classes and presentation layer controller’s constructors. Constructing an object using interfaces as parameters decouples our solution from concrete classes and gives us flexibility to add in AOP proxies, swap out modules, and inject mocked modules for testing.
The ASP.Net MVC framework allows us to plug-in our dependency injection (DI) logic directly to the framework, allowing controllers to benefit from interface parameter injection in their constructors just like our service and data layer classes. Even complex objects with several layers of dependencies can be constructed by the IoC. Imagine our Presentation Layer controller, in it’s constructor it may depend on multiple service layer classes, those service layer classes may depend on a number of repositories in our data layer as well as logging and caching via AOP injection, our data layer classes may depend on a data context also managed by the IoC, etc. Consider the following:
namespace Client.Core.Services
{
public class HomeController : Controller
{
public HomeController( IProfileService profile )
{
In the above example our IoC container creates our IProfileService object, assembles all the dependencies of all the related objects and maps interfaces like IProfileService to a concrete implementation in our service layer. This technique obviously saves a lot of instantiation code which DRY’s up our classes, and it also removes the chance of making small errors like using the wrong connection string on a context. We’ve used many IoC containers, but we’ve settled on StructureMap for our base template.
MVC
Many people love to point out that MVC, model, view, controller architecture, is nothing new. It’s been around for decades, but Ruby on Rails can be thanked for MVC’s popularity today. Regardless of the language you use, there’s probably an MVC framework available for web development, and it probably shares many of the conventions established in Rails.
As ASP.Net WebForms developers since 2001, we were reluctant to embrace MVC at first. You mean we have to write all the javascript again?!? Isn’t that why we paid thousands of dollars for the Telerik WebForms controls?
Today many of the original barriers to MVC adoption on the Microsoft platform have been removed. The major control vendors all have MVC solutions and the open source community quickly consolidated around solid JS controls (as well as benefiting from the contributions of .Net developers who suddenly found themselves sharing JS controls with Java, Ruby, Python, and PHP developers).
Our Abstraction Layers
Now that we've covered dependency injection and MVC, let's dive into our application template's architecture. Our template's architecture is divided into three logical layers:
- Data Access Layer
- Services Layer
- Presentation Layer
Data Access Layer
We prefer to use Object Relational Mapping (ORM) layers with a Repository and Unit of Work pattern. Many of the leading ORM components support these patterns, but lately we find ourselves using Entity Framework (EF). After years of ignoring EF and using other ORM options like NHibernate and LLBLGen, we finally decided to give EF 4.1 a second chance. The reason we changed our mind? EF 4.1 (and community contributions in codeplex) added the support for POCO’s, plain old C# objects, to be generated from the EF designers. When your ORM supports 95%+ of Linq expressions, generic collections, and generates POCO’s you’re in good shape. By returning POCO’s representing our domain entities (tables and views) we keep the knowledge of the specific, concrete data access layer from leaking into other layers. This separation of concern is a key principle of Interface Segregation.
We don’t like the other layers of our application to depend on any data access assemblies. Imagine if your Data Access Layer Interfaces returned objects like ObjectSet<Person>. ObjectSet is an Entity Framework specific collection class. In order to consume that class in our service we now have to link to EF in the Service Layer. Working with domain objects and collections in the service layer makes testing and mocking harder because these objects are typically more complex than generic POCO collections. Further, we don’t have to force our Data Access Layer (DAL) callers to be familiar with these domain-specific interfaces. Developers who have never used EF can use our simple POCO’s and generic collections with ease.
It’s worth mentioning a controversial topic, IQueryable<>. IQueryable is incredibly useful due to its deferred execution and ability to compose filtered queries, but many people believe that passing IQueryable<> from the data layer to the service layer constitutes a leaky abstraction. Their argument is that IQueryable<> isn't an enumerated result and isn't guaranteed to work in all cases since data store drivers have varying levels of support for Linq. The burden of implementing IQueryable<> is so great that it seems to violate SOLID principles as a return value. Further, it's conceivable that users could pull more data than you expect via dynamically included relations and that queries would pass or fail depending on the backing data store. We only return IQueryable<> results from our DAL when we work with dedicated MS shops on SQL Server using EF, and generally then we do it to be pragmatic. The advantage in this case is that callers can receive a query and then additionally filter the domain results based on our POCO properties without even being aware of the underlying database we are using. This deferred execution filtering is handy in the services layer when we want to filter results or join and project results into view models.
While the majority of our Data Access Layers use ORM, we still on occasion have to go to the bare metal SqlConnection or sometimes the Patterns and Practices Data Application Block. Those situations can still be handled via our DI strategy, using constructor injection to our DAL classes. NoSQL stores are also a popular solution and work well in our DAL's. Finally, an emerging trend is the rise of micro ORM's. These micro ORM's are gaining popularity in high-traffic sites where heavy ORM's like EF and Linq To Sql are too slow. Micro ORM's represent a thin layer over a database connection and typically run a query and pull results into POCO collections. Look for a performance article from us soon on our favorite micro ORM's like Dapper (StackOverflow), Massive, and PetaPoco.
Conclusion
In our next article we will discuss the Services and Presentation layers of our ASP.Net template.
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.