Let's say you're creating an application, maybe a web-based ERP, using the now ubiquitous MVW (Model-View-Whatever) pattern. Let's also assume that, like many real-world applications using this pattern, the classes that constitute your problem domain (i.e. your Domain Model or DM) cannot effectively be directly consumed by your views. In this case, your views will consume separate classes that use data from your Domain Model but include properties and methods specific to the view (i.e. View Models or VMs).
This has become a very common practice in modern development. Especially in applications using layered architecture or Domain-Driven-Design (DDD), having a separate set of classes for domain and presentation is not only logical, it is often essential for maintainability and scalability.
Often, though, View Model classes turn out to be very similar, if not nearly identical, to the Domain Model classes. The challenge in this situation then becomes deciding the best method for relating the Domain Model to the View Model. Several techniques have been developed to address this problem. The most common are Field Mapping, Composition, and Inheritance, which I will describe in this article.
The simplest method of relating your Domain and View Models is by direct field mapping. This method works best in cases of what Dino Esposito calls "anemic" models, which are models that contain mostly data (i.e. properties) and little or no functionality (i.e. methods). In this case, your View Model can be created simply by copying property values from your Domain Model.
Initially you may want to copy property values directly by assignment. However, this could get tedious and even error-prone if there are a large number of properties in each class. There are several tools available to help facilitate this process. My personal choice is AutoMapper™, but many alternatives exist.
Another common approach is to use composition, in which one or more Domain Model objects are members of your View Model object.
In the example below the View Model, UserVm, contains a public property for a Domain Model object. The view can therefore access properties and methods of the Domain Model through this property.
One benefit of using composition is that you can have more than one Domain Model object associated with a View Model. In the example below a form view references the class CreateUserVm as a View Model. In the Domain Model, however, creating a new user involves creating two new domain objects: an account (user name, password, etc.) and a profile (name, address, phone number, etc.). By using composition, one View Model can contain a reference to each Domain Model object needed.
One caveat of the approach is that the Domain Model classes are completely exposed to the view. To an extent this violates the separation-of-concerns (SoC) concept behind having a layered architecture in the first place. This means that refactoring your Domain Model class now requires refactoring one or more views, and when using composition it may be difficult to find all the instances where this applies. There may also be security concerns to consider with exposing the entire domain object.
There is another way of using composition that addresses some of the concerns with exposing the Domain Model. The View Model can reference one or more Domain Model classes through private members.
In the example below, the Domain Model object, User, is passed to the View Model object, UserVm, through the constructor and held by a private field. Properties and methods of the Domain Model must therefore be explicitly exposed through public members of the View Model.
In most cases, the View Model exposes one or more Domain Model members by proxy. For example, a call to UserVm.FirstName would likely return the value of _user.FirstName. Here the developer of the View Model could choose not to implement a setter for this property, thereby protecting the Domain Model from changes. Sometimes, though, the View Model can consolidate or filter information. For example, a call to UserVm.GetFirstNameForFriends() might loop through a collection exposed by _user.GetFriends() and only return the first names of the Domain Model collection.
A less common and arguably trickier method is to use inheritance. With this technique, your View Model inherits from a Domain Model class. Normally you would only use this approach if you needed enhance or override functionality from your Domain Model within or View Model. This is very rarely necessary in real-world applications, and almost always violates SOLID principles in some way. Most of the benefits of inheritance can also be gained through composition.
However, there are circumstances in which inheritance does make sense and should be considered. In this case, there are pros and cons specific to every implementation that should be carefully evaluated.
Although very common in MVW applications today, there is actually no hard-and-fast rule dictating that your Domain Model cannot be used as your View Model. In fact, most tutorials, examples, and hello-world applications use one anemic model in all layers of the application. If your application domain is simple enough, this is probably fine. However, be aware that as the complexity of your application increases, so will the reasons for creating separate layers and models. Whether to do so at the beginning of a project or to refactor later is an architectural decision.
In the end, architectural decisions like this must be evaluated according to the specific requirements and environment of the individual project. Each of the above techniques has pros and cons, and if you ask 10 developers you'll get 12 opinions. Often you may even use different approaches for different View Models within a single project.
Ultimately, the responsibility for choosing the most appropriate coding practice for a given situation is up to the architects and developers of the project. As statistician George Box once quipped, "All models are wrong; some models are useful."
And always remember TBYC: Think Before You Code. :)
Last modified: 6 October 2014.