how do i adapt a simple vs complex model
When I started with separated presentation patterns, I always thought that to create a single screen, I ALWAYS had to create a view to display the data, a presenter/controller/viewmodel class to hold the logic and a model class to hold the data. Later, I learned that this was a too rigid way of thinking about these classes and that I had to take a more pragmatic approach. Not every model is the same, and so you might have to take different choices. Sometimes you can work with a very simple model, that’s very well suited to your views. Great.. but that requires a completely different approach from working with a complex model. This blog post goes into more details about working with different types of Models.
What is that model thingy?
A model is more than just a POCO class that holds data. I view the model more as the collection of classes that solve a particular business problem. This can include:
- A Business entity that stores the data.
- A Business component that holds business logic and operates on top of a business entity.
- Classes that retrieve or persist the data, for example repositories, service agents, etc.
A colleague of mine told me the following analogy, which I thought was striking. The classic Model View Controller paradigm can be thought of as batch processingThe Controller handles the input from the user and how to respond to that input. The View handles everything related to showing the output to the user. So the model encompasses the data and all the processing that is done with that data.
So when you design your model, you should try to model it so it can optimally accommodate the business logic and business rules. The UI is not the only way you might want to have access to your business logic. For example, an import process or web service might need to call the same Business Logic as the UI. Also, you should be able to change the process followed in the UI, without affecting the data and the business logic your application works with. For example, to improve user experience, you might change a complex editing screen into a wizard or more graphically oriented editing. If you modeled your Model around your UI, then you have to change your Model when the UI changes. When you have designed your Model around the business logic, then your ViewModel can adapt the model to the process the user follows.
Now of course there are tradeoff’s. If you have very little business logic, and the UI is the only consumer of the business logic, then it might make more sense to design the model to more closely match what you see on the screen.
Working with “easy to use” models
Sometimes, you have the fortune to get really easy to work with models. Easy to use models typically have the following characteristics:
- They are easy to bind to.
For example, they implement INotifyPropertyChanged for properties, ICollectionNotifyPropertyChanged for all collections of objects. Also, they might implement validation logic in a way that’s easily consumed by the UI, such as implementing IDataErrorInfo.
- The model corresponds closely to what you see on the screen.
If the model is very similar to the data on the UI. For example a Customer screen displays and edits all the fields from the Customer model object. In that case, there is very little need for data conversion or aggregation.
Usually, you’ll be working with a very easy to use model if you have full control of the design and implementation of the model. Then you can design the model to very closely fit the application scenario. Another situation where you’ll be working with easy models is if your application has little business logic and is mainly used for editing and displaying data.
Let’s consider the following example:
You’d like to display a view on the screen that displays information about a Meal and it’s Ingredients. You have designed the Model yourself and it will only be used for your application. Because of the way you designed the model, supports 2 way databinding and it’s very well suited to display things on the screen. However, there is some aggregate data, such as total calories that the model does not provide.
In this case, you can design your UI layer like this:
The model itself contains most of the information that needs to be displayed on the screen, so the view can directly consume the model classes and show most of the information based on that. However, since there is some aggregate data and logic, you will need a viewmodel to hold that. Also, the viewmodel can hold UI state information that shouldn’t be present in the model, such as the currently selected ingredient.
As you can see, the IngredientsView doesn’t have a ViewModel of it’s own. It doesn’t have any logic of it’s own and can get all the information it needs from the Model. Then, it doesn’t make sense to create a separate ViewModel for that view. All the logic and the composition is handled by it’s parents.
So there are a couple of things you can do when you work with easy to bind models:
- Consuming the model directly from the View
If your Model exposes most of the data your view needs, then the view can directly consume the model. Be careful if your model changes often (IE, if you don’t have control over the model because it’s created and exposed by an other team or even other company). In that case, consider if it makes sense to absorb those changes in your ViewModel.
- Don’t blindly create viewmodels for all of your views.
If you don’t need one, because there is no Logic or UI state to hold, then don’t create one. However, if you find that you have some logic or UI state, then it definitely makes sense to create a simple ViewModel.
Working with “hard to use” models
Unfortunately, you don’t always have the luxury to work with models that are easy to work with. A hard to work with model has the following characteristics:
- The model does not easily map to what you need in the UI. For example, the view needs to combine information from several models.
- It does not allow two-way databinding.
This can happen if you don’t own the model. For example, you are consuming 3rd party web services or you are working with a database and model from a different application. In these cases, you often don’t have control over what he model looks like. Now you can solve the problem of compensating for a difficult model completely in the view, but that makes the view very complex. And since view code is very hard to test and is also very closely tied to the UI technology, this can quickly turn into a management nightmare. It’s much better to solve this problem in a separate, testable set of classes, that together form the ViewModel.
Lets consider an example again. Again you want to display information about how healthy a meal is. Unfortunately, the meal and ingredient classes don’t contain this information, so a service called the MealPlanner is used to determine if a meal is healthy for you or not. In this case, the data from the model cannot be used directly.
In this case, the model is not suited well to show the required information from the screen. And because the Model is already used in many other parts of your application, it’s not easily changed. So a new data structure has to be created, to contain the aggregated information. This new datastructure, the MealViewModel and the IngredientViewModel, are now very well suited to display all the required information on the screen.
So, if you are working with harder to use models, you might have to create new datastructures that will hold data in a form that’s very easily consumed by the view.
I hope this makes the role of the models and the ViewModels a bit clearer.