Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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?

share|improve this question
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. – Alex Schimp Sep 6 '12 at 23:08
@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.) – David Sep 7 '12 at 12:06

3 Answers

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.

share|improve this answer
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. – David Sep 19 '12 at 19:20
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. – MikeC Sep 19 '12 at 19:26

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 :)

share|improve this answer
1  
+2 if I could. This shows the different way of thinking about viewmodels vs models. Something I am still learning as I go! – James Khoury Sep 20 '12 at 3:06
@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. – dreza Sep 20 '12 at 3:59
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. – David Sep 20 '12 at 12:18

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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