27
\$\begingroup\$

I have a central domain assembly which contains various rich domain models. Lots of business logic, etc. To keep this example simple, here's probably the simplest one:

public class Location
{
    private int _id;
    public int ID
    {
        get { return _id; }
        private set
        {
            if (value == default(int))
                throw new ArgumentNullException("ID");
            _id = value;
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentNullException("Name");
            _name = value;
        }
    }

    public string Description { get; set; }

    private string _address;
    public string Address
    {
        get { return _address; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentNullException("Address");
            _address = value;

            GeoCoordinates = IoCContainerFactory.Current.GetInstance<Geocoder>().ConvertAddressToCoordinates(Address);
        }
    }

    public Coordinates GeoCoordinates { get; private set; }

    private Location() { }

    public Location(string name, string address)
    {
        Name = name;
        Description = string.Empty;
        Address = address;
    }

    public Location(int id, string name, string description, string address, Coordinates coordinates)
    {
        if (coordinates == null)
            throw new ArgumentNullException("GeoCoordinates");

        ID = id;
        Name = name;
        Description = description;
        Address = address;
    }

    public class Coordinates
    {
        public decimal Latitude { get; private set; }
        public decimal Longitude { get; private set; }

        private Coordinates() { }

        public Coordinates(decimal latitude, decimal longitude)
            : this()
        {
            Latitude = latitude;
            Longitude = longitude;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (!(obj is Coordinates))
                return false;
            var coord = obj as Coordinates;
            return ((coord.Latitude == this.Latitude) &&
                    (coord.Longitude == this.Longitude));
        }

        public override string ToString()
        {
            return string.Format("Latitude: {0}, Longitude: {1}", Latitude.ToString(), Longitude.ToString());
        }
    }
}

For a number of reasons, I don't want to use these domain models as my presentation models in my MVC application. At first I was just creating very similar DTOs for the models to use as presentation models. Something like this:

public class LocationViewModel
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Address { get; set; }
    public decimal Latitude { get; set; }
    public decimal Longitude { get; set; }
}

However, that doesn't make sense for every view situation. A Create action, for example, shouldn't have an ID property. A Delete action doesn't need all of that information. And so on.

So now I'm ending up with presentation models that are one-to-one with the presentations themselves. Something like this:

public class LocationCreateViewModel
{
    public string Name { get; set; }
    public string Description { get; set; }
    public string Address { get; set; }
}

public class LocationDetailsVieWModel
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Address { get; set; }
    public decimal Latitude { get; set; }
    public decimal Longitude { get; set; }
}

And so on, customized for the views that bind to them. This became further useful as I could use data annotations to make cleaner use of the ASP.NET MVC tooling. Something like this:

public class LocationCreateViewModel
{
    [Required]
    public string Name { get; set; }
    public string Description { get; set; }
    [Required]
    public string Address { get; set; }
}

These can get more complex, but the point is that I'm keeping them on the presentation models because I don't feel they have a place in the business domain. I think it would be misleading to have a [Required] annotation on a class property if it doesn't actually make it required unless interpreted by a very specific set of tools. And since lots of other things use these domain models, not just this one MVC website, then I want to make sure the logic is really baked in to the models and not loosely applied for an assumed set of tools.

A recurring piece of functionality in this setup is to convert between presentation models and domain models. So presentation models which need to convert to domain models have instance methods on them:

public Location ToDomainModel();

And presentations models which need to be built from domain models have static methods on them:

public static ConstructFromDomainModel(int id);
public static ConstructFromDomainModel(Location location);

The original goal in all of this was to separate concerns. A lot. But I wonder if I've taken a wrong turn in that effort. This isn't necessarily an unmanageable amount of code, but I don't want it to become unmanageable. There's sort of a "class explosion" going on as more and more gets added. And the unit tests are increasing at an even faster rate (which will become a question all its own once I sort out this question).

Is there a "better" way? Are there known patterns which would be better followed and still maintain the separation of concerns in a tool-agnostic approach?

\$\endgroup\$
  • \$\begingroup\$ I wonder if something like AutoMapper would work for you. It probably wouldn't completely solve the problem though, as it would only remove the need for conversion methods. BTW, very good question - I'm interested to hear what others have to say as well. \$\endgroup\$ – Alex Schimp Sep 6 '12 at 23:08
  • \$\begingroup\$ @AlexSchimp: I thought about AutoMapper at one point and may re-visit it. Another thing to note on this implementation is that it frees me to de-couple the LocationViewModels from the Location in yet another way. The view models are coupled to the views they populate, and can be highly customized for those views. So a single view model might be a composite of a handful of models for a more complex view. (For example, say a Location-based view also needs some Event data or User data or something else. Rather than send all the models, I make a lighter composite view model.) \$\endgroup\$ – David Sep 7 '12 at 12:06
  • \$\begingroup\$ @David Does you domain model have 1:1 mapping with your data model (DB) ? \$\endgroup\$ – lbrahim May 19 '14 at 9:36
11
\$\begingroup\$

I was just playing around with this concept today. I have a User class defined in another assembly. Then I created three classes "based on" (but not derived from) that User class: CreateUser, EditUser, and DetailsUser. Each contains View-specific DataAnnotations (Required, DataType, etc.).

public class CreateUser
{
    [Required]
    public String FirstName { get; set; }
    [Required]
    public String LastName { get; set; }
    [Required]
    [DataType(DataType.EmailAddress)]
    public String Email { get; set; }
    [Required]
    [DataType(DataType.Password)]
    public String Password { get; set; }
    [Required]
    [DataType(DataType.Password)]
    public String VerifyPassword { get; set; }
}

CreateUser has no ID, and has an extra property, VerifyPassword. My validation logic ensures that VerifyPassword==Password. There is no ID property, because it's a new User. After validation in my Create action, I can then map it to a User and add it to my data store.

public class EditUser 
{
    [HiddenInput(DisplayValue = false)]
    public int Id { get; set; }
    [Required]
    public String FirstName { get; set; }
    [Required]
    public String LastName { get; set; }
    [Required]
    [DataType(DataType.EmailAddress)]
    public String Email { get; set; }
}

For Edit User, I pull the user from the database, and map it to an EditUser object. EditUser has a read-only and hidden ID, and no Password properties. MVC's model binder prevents anyone from injecting properties on the User object that don't exist on the EditUser object.

public class DetailsUser
{
    [HiddenInput(DisplayValue = true)]
    public int Id { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
    public String Email { get; set; }
    public String Password { get { return "Not Shown"; } }
}

For DetailsUser, I do something similar, again hiding the Password property.

You are right about the class explosion. However, each class is very tiny and self-contained. The nice thing about keeping all of this in the ViewModels is that I am free to use Html.EditorForModel() in my views. For me, the choice is extra code in my ViewModels, or extra code in my Views. It's up to you where to put it.

It does seem to violate DRY, having multiple User-based classes with duplicate properties. I thought perhaps they could derive from a common class, and maybe even User itself. I'm still thinking on that, and am open to thoughts and suggestions.

As for the mapping, I have been playing around with the Moo project (https://github.com/dclucas/MOO). It has a simple mapper that I find easier to use than AutoMapper.

var editUser= user.MapTo<EditUser>();

This creates an EditUser object from an existing User object, provided the property names match.

\$\endgroup\$
  • \$\begingroup\$ It sounds like we're reaching the same conclusion about the DRY violations. Right now my reasoning is that it's doing the same things but for very different reasons, which is a bit of a borderline case. Certain changes would propagate across many objects, but many changes would be isolated to the tiny classes with their tightly-scoped responsibilities. As for deriving from common classes, I have a tendency to be very hesitant to jump to inheritance in general. Inheritance often feels like tight coupling to me. \$\endgroup\$ – David Sep 19 '12 at 19:20
  • \$\begingroup\$ One thing I neglected to mention is that I typically use Entity Framework for my data layer. I haven't figured out how to take the partially-completed EditUser object and tell EF to update only the columns/properties listed. When mapping back to the User object, all of the missing properties end up being null. \$\endgroup\$ – MikeC Sep 19 '12 at 19:26
  • \$\begingroup\$ Can you please provide an example for the code in the controller say, for the edit action. Do you have to query the database with the User model and then immediately before displaying convert the User model to the EditUser model? \$\endgroup\$ – Danny Rancher Nov 26 '14 at 17:18
11
\$\begingroup\$

I don't believe there is a "right way" or a "wrong way" as such (well maybe there is a wrong way :)). I think it all depends on the context of the situation and what is required.

However, I've always been a fan of using ViewModels and DTO's so will suggest that the approach you are doing is a "accepted" way. When I first used this approach I had the same kind of problems that you mention in that some views shared data sets and I didn't want to duplicate those properties everywhere. My approach in this instance was to use inheritance. However, I ended up often having 3 levels deep and any changes to objects became difficult after a while.

In retrospect of that I am approaching ViewModel creation slightly differently these days. I've read a few articles that suggest you should just have one big flat ViewModel and duplicate the properties as required. This means the ViewModel is specific to your need and although you may have a slight class explosion and duplication you can be confident when changing one viewModel you will not effect anything else in the project. Also tools such as AutoMapper (as suggested by Kevin) help with not having to worry about the mapping between Model and ViewModel anyway.

However I still like the thought of sharing common information so an alternative approach is to break down the properties into sub ViewModels. So in your example above you could take this approach:

Create a bunch of Viewmodels that contain explicit separation of data concerns:

public class UserInformationViewModel
{
    [Required]
    public String FirstName { get; set; }
    [Required]
    public String LastName { get; set; }
}

public class UserContactDetailsViewModel
{
    [Required]
    [DataType(DataType.EmailAddress)]
    public String Email { get; set; }       
}

public class UserPasswordViewModel
{
    [DataType(DataType.Password)]
    public String Password { get; set; }
    [Required]
    [DataType(DataType.Password)]
    public String VerifyPassword { get; set; }
}

Now create using composition any top level view models for the different view requirements of the system:

public class CreateUserViewModel
{
    private UserInformationViewModel _information;

    public UserInformationViewModel Information
    {
        get { return _information ?? (_information = new UserInformationViewModel()); }
        set { _information = information; }
    }

    private UserContactDetailsViewModel _contactDetails;

    public UserContactDetailsViewModel ContactDetails
    {
        get { return _contactDetails ?? (_contactDetails = new UserContactDetailsViewModel()); }
        set { _contactDetails = information; }
    }
    private UserPasswordViewModel _password;

    public UserPasswordViewModel Verification
    {
        get { return _password ?? (_password = new UserPasswordViewModel()); }
        set { _password = information; }
    }   
}

public class EditUserViewModel
{
    [HiddenInput(DisplayValue = false)]
    [ReadOnly(true)]
    public int Id { get; set; }

    private UserInformationViewModel _information;  
    public UserInformationViewModel Information
    {
        get { return _information ?? (_information = new UserInformationViewModel()); }
        set { _information = information; }
    }

    private UserContactDetailsViewModel _contactDetails;    
    public UserContactDetailsViewModel ContactDetails
    {
        get { return _contactDetails ?? (_contactDetails = new UserContactDetailsViewModel()); }
        set { _contactDetails = information; }
    }   
}

// For details view I would use either inheritence or simply add the Verification attribute onto a new
// class.  Lets go with inheritence for now
public class UserDetailsViewModel 
{
    private UserPasswordViewModel _password;

    public UserPasswordViewModel Verification
    {
        get { return _password ?? (_password = new UserPasswordViewModel()); }
        set { _password = information; }
    }   
}

Mapping model to ViewModel

As Kevin has suggested there are great tools out there already that do this for you. I haven't personally used any of them but I have heard good things about AutoMapper.

Making use of Partials for sub view models

Because we have now separated the different elements into components I would consider creating a different partial view per view model. This way even your views become re-usable and you share common view presentation around.

i.e

UserContactDetailsViewModel => _UserContactDetails.cshtml
UserInformationViewModel    => _UserInformation.cshtml
UserPasswordViewModel       => _UserNewPassword.cshtml
UserPasswordViewModel       => _UserEditPassword.cshtml

Doing this each view requirement would be a case of incorporating the partials in as required.

Pitfalls on view model composition and partials: On problem with this approach I had was that when using these viewModels within the views the resultant elements created meant that the bindings did not come back on posts.

What was happening was that I would render a partial as such

@Html.Partial("_UserInformation", Model.Information)

However that was great until I looked at the resultant html elements created.

<input id = "Firstname" name="Firstname" type="text" /> // etc

The problem here is that on binding back there will be no Firstname element on our encapsulating viewModel. What the input really should have looked like was

<input id = "Information_Firstname" name="Information_Firstname" type="text" /> // etc

What I ended up doing was creating an extension method that would handle this for me without worrying about any of this. So I ended up righting code like so to generate the correct element naming.

@Html.Partial("_UserInformation", model => model.Information)

As for how that works, I'll leave you to figure that one out, or I might post it if you go down this route? However what that partial does is ends up printing exactly what you need:

<input id = "Information_Firstname" name="Information_Firstname" type="text" /> // etc

Summary I like your approach. You are correct in that class explosion might be a problem but I think this potentially is outweighed by the clear separation of concerns in the system. As for TDD, I don't think every class in an application needs testing. For example if you classes are DTO's there's nothing to test, so class explosion might not necessarily be a cause for concern (or a reason to not go down this route).

Well just my two cents. I hope you get some answers and reviews that enable you to produce code that you are happy and proud of. After all, isn't that what we are after :)

\$\endgroup\$
  • 1
    \$\begingroup\$ +2 if I could. This shows the different way of thinking about viewmodels vs models. Something I am still learning as I go! \$\endgroup\$ – James Khoury Sep 20 '12 at 3:06
  • \$\begingroup\$ @JamesKhoury cheers James. I'm constantly learning too. One of the reasons I joined this site to get good feedback and different ideas/opinions and better ways of doing things. \$\endgroup\$ – dreza Sep 20 '12 at 3:59
  • \$\begingroup\$ You make a clear point on favoring composition over inheritance, which I've found to usually be a good approach. (Which I believe was also in the Gang Of Four book.) I also like the idea of taking that same philosophy to the views much more than is normally prescribed, I'll have to give that a try as well. For TDD, I guess I'm hooked on getting to that 100% coverage, but you're right in that DTOs don't need to be tested directly. I should instead test the functionality that uses them, and if those tests leave parts of the DTOs uncovered then I don't need those parts anyway. \$\endgroup\$ – David Sep 20 '12 at 12:18
  • \$\begingroup\$ This is resurrecting a very old thread, but I find that a better way of dealing with the components of a view are to create editor templates, then use @@Html.EditorFor or display templates and use @@Html.DisplayFor. Very similar to using partials, but MVC handles the logic when you post. It also handles the logic when there are 1-many relationships/collections \$\endgroup\$ – Carl Jan 21 '18 at 0:41
6
\$\begingroup\$

In a nutshell there are THREE types of model classes to consider:

  1. Data Model classes - These are classes that are used for reading/writing data to/from storage. These classes almost always match your table schemes exactly, plus some navigation properties. If you use entity framework (you should), you will have DBSets of these classes defined on your DbContext. These classes almost never contain any code.

  2. Presentation classes - These are basically denormalized versions of your data model classes that are used to display or report data. For example you may have an Order data model class that contains a CustomerID and an OrderDate. The Customer name, however, is defined on the Customer data model class. If you want to display a grid that contains Customer Name and Order Date you may want to use a presentation class that is composed of selected properties of both the Order data model class and the Customer data model class. The only code these classes usually contain is a constructor that allows the class to be instantiated from one or data model classes and perhaps a method or two to construct a data model class from the presentation class. Avoid defining business logic code on these classes.

  3. ViewModel classes - These classes are backing classes for views. Think of them as code behind. The term "Model" in the name is a bit misleading because these classes have less to do with your data or presentation model and more to do with providing properties to make your views work correctly. Unlike Data Model classes or Presentation model classes, ViewModel classes often contain code to react to user events.

    ViewModel classes contain properties for helping the user input data. For example, on an order entry screen the ViewModel may contain a property which is a collection of OrderTypes. This collection may be used to populate a dropdown list. The ViewModel may also contain a OrderType property which is used to store the currently selected OrderType. By comparison, the Order presentation class may contain a property of type string that simply displays the name of the OrderType. On the Data Model class the OrderType may be represented by an integer which is a foreign key to the OrderType table. You should avoid defining business logic on your ViewModel classes. Make calls to your business logic layer instead.

For more info on this subject and for a complete article on how to define a repository and business logic layer, please see the article A service oriented approach to implementing repository pattern with Entity Framework, MVC, and MVVM on my web site www.samwheat.com.

\$\endgroup\$
  • \$\begingroup\$ In some cases, there are also "domain model" or "business model" classes. They are somewhere between your data and presentation classes. \$\endgroup\$ – Tom Pažourek Sep 21 '16 at 7:37
  • \$\begingroup\$ @TomPažourek I think the term "domain model" is the correct one... it is what I mean when I say data model. \$\endgroup\$ – Sam Sep 22 '16 at 22:21
  • \$\begingroup\$ I'm just pointing out that in some cases you have separate models for reading/writing to storage and separate models that you do business logic with. In those cases it could be considered as fourth kind of models. But again, different projects will need different abstractions and it's not always beneficial. \$\endgroup\$ – Tom Pažourek Sep 23 '16 at 5:00
3
\$\begingroup\$

As @Alex Schimp suggested AutoMapper is a an excellent tool for this scenario and exactly what it was built for. I use AutoMapper all of the time to translate between domain models and view models and it greatly simplifies the process and eliminates a lot of coding for the translation between the two. It does a great job of handling the mapping, especially if you keep the field names the same between the domain and the view. So in the example of where the view may not need the ID then AutoMapper will figure out that it does not need to map it to the view just because it is not present. The other advantage of this is if your models change you do not have to remember to update your translation code as well. As long as there is a clear mapping that AutoMapper can figure out then the translation layer is automatically handled. There are also methods for more advanced translations in AutoMapper if the default ones are not sufficient. Your approach of using DTO's is excellent and will pay off in the long run for ease of maintenance, extensibility and scalability. Martin Fowler discuses the benefits of the DTO design pattern in his book Patterns of Enterprise Application Architecture.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.