Although this article was written primarily to help ASP.NET developers make the conceptual transition from WebForms to MVC, the broader architectural concepts involved could help many developers and software engineers think critically about the overall design of complex systems. In this article, I will try to demystify one of the concepts within the MVC paradigm that gives even some of the most experienced and mentally agile developers a bit of trouble: namely, the Model.
Most of the tutorials and introductions to using ASP.NET MVC start with the basic and deceptively simple diagram shown in Figure 1. And for those new to programming this is a great start. However, those of us that have years of experience in creating N-Tier enterprise-level applications are more familiar with the architectural pattern in Figure 2. (In this article, I will use "N-Tier" or "N-Layer" to describe the logical architecture, though many purists will insist the term "N-Tier" applies only to physically distributed applications.) As you will quickly notice, these patterns don’t look much a like - at least not on the surface.
In truth, we’re really not comparing the the same thing here. MVC as a design pattern is not meant to replace an entire architecture of this scale - at least not completely. The pattern in Figure 1 is simplified and generalized and is more suited to simple, relatively small applications.
So how do we expound upon that first MVC diagram to bring it more in line with the layered N-Tier architecture we’ve come to know and love? As the article title implies, our answer lies in a deeper understanding of the “model”.
A common interpretation of MVC architecture in comparison to N-Tier architecture is to simply equate the boxes in the diagram. Most often, the controller is assumed to represent business logic, while the model represents data access, and of course, the view is the user interface. Though this seems logical, it isn't very accurate - or useful.
A more accurate analogue for the controller would be an event handler. The controller is responsible for handling the user request or input and sending the appropriate response - be it view, JSON, redirect, etc. As such, it is actually part of the Presentation logic.
We already know, however, that business logic should not be in the View. Therefore, then, business logic belongs in the Model. But wait, isn't the Model where the data access belongs?
This is where MVC aficionados get the phrase "thin controller, fat model". (No, this isn’t encouraging eating disorders among design elements.) The model is the backbone of our application and is meant to contain the meat and pototatos of the functionality.
But as the application gets larger and more complex, so would the model. That one box is going to get much larger than the others. Too large. How then, do we keep that box - the Model - from becoming a big ball of mud? Easily: We split that box into smaller boxes. We divide the Model ...
The models presented in tutorials are little more than data structures - simple sets of properties that correspond to data fields, each property annotated judiciously with rules regarding data and display. The reason all these tutorial models are so simple is because the domain of the tutorial itself is simple. (In software engineering, the "problem domain" or "business domain" is basically all the information-data and logic-a system needs to perform its function.) The domain here consists of a few simple objects that need to be stored in a database and presented to the user. And nothing else.
So in a simple application, this Domain Model is suitable for the entire application. We can use an ORM like Entity Framework to persisted domain objects into a data store, and the same domain objects can be easily bound to views for display or user input. This Domain Model can handle business logic, user interface, and data access because all of these functions are relatively simple.
Usually the first place a Domain Model becomes inadequate is when a particular view needs more information than a single domain entity. A single complex view may need to display information from multiple related entities. At this point the concept of a View Model becomes handy. A View Model can represent any part of the entire domain that a given view requires.
Most commonly, a View Model will use aggregation to bring two or more domain objects together. For example, if a view requires both user information and product information, the View Model might reference a User object and a Product object, therefore exposing both domain entities and their business logic to a single view.
Another approach to creating View Models is to use inheritance. If the View Model derives from a Domain Model object, then information and functionality can be added to existing properties and methods. In addition, customizations can be made by overriding members of the Domain Model.
As our application gets more complex, another logical separation becomes apparent. This is especially true if we are using an ORM for data access with a complex domain object. At some point, the complexity of the domain just can't be easily translated into simple data entities.
Even if we are not using an ORM, our domain might not naturally equate to our data storage. For example, our database my be optimized in a certain way that makes data access more efficient, but does not necessarily match our domain. Or we may have added quite a bit of logic to our domain objects that make automated data storage difficult.
At this point, we need to separate our Data Model from our Domain Model. Believe it or not, if you've used ADO (or even DAO, for those of us that have been at it a while), then you're already quite familiar with this concept. In ADO, our Data Model is represented by DataSet and DataTable objects. These objects usually represent the data schema fairly closely, but can be manipulated with joins, views, and other SQL techniques to create data structures more useful for particular actions.
Even though Microsoft and others have encouraged the trend to use Entity Framework, there is no reason we can't choose to continue using ADO for our Data Model in MVC. But these aren't our only options. For instance, we could use a Repository pattern or any of many other valid data access patterns. Regardless of the means we choose for data access, the role of the Data Access layer is the same. The domain objects must be translated into the data objects that will be be persisted.
Now that we see how the "Model" in MVC can be differentiated, we can start to think of an MVC application in terms of an N-Tier Architecture. In Figure 3, we have an architectural diagram that gives us the typical three layers of an enterprise application. The top layer, the User Interface, is responsible for handling user interaction. This includes request and event handling (through Controllers), the data and logic required to process the request (in the View Model), and the response to the user (the View). The Business Logic Layer is our Domain Model. And our Data Access Layer comprises our Data Model and the logic necessary to communicate between the data store and the rest of our application. Now we have a useful translation of N-Tier Architecture using the Model-View-Controller design pattern.
Note that the Diagram in Figure 3 does not contain some of the common elements of enterprise-level applications, such as a security model, logging, or other cross-cutting concerns. For clarity, I've only included those components applicable to this article. Depending on the requirements of a given system, the complete architectual diagram may be much more involved. (Though we continue to try, no one has yet designed a one-size-fits-all application template.)
And even though we will rarely have as many as 3 classes of Model, we aren't limited there. There are scenarios where, conceivably, our domain would need further differentiation. Perhaps we need another data structure with which to interact with a web service. Or maybe we want to expose an API that exchanges JSON objects that define their own version of the domain. In most cases, though, dividing your architecture into Domain, View, and Data Models will be sufficient to realize the benefits of an N-Tier architecture while still moving forward with the MVC pattern.
And always remember TBYC: Think Before You Code. :)
Last modified: 15 April 2014.