When using reusable business objects, what is considered best practice when building view models?
We use an object we call Builder
to build our view models. One builder for each logical unit of views (orders, users etc), where each unit can contain a number of different view models (orders contains summary, order lines etc).
A builder may pull data through one or more standard business objects in order to build a view model.
What is considered the better practice when it comes to using business objects/models in view models?
Approach 1
Allow the use of business objects in the view model?
//Business object in some library
public class Order
{
public int OrderNum;
public int NumOrderLines;
//...
}
//Order builder in website
public class OrderBuilder
{
public OrderSummary BuildSummaryForOrder(int OrderNum)
{
Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
//Any exception handling, additional logic, or whatever
OrderSummary obModel = new OrderSummary();
obModel.Order = obOrder;
return obModel;
}
}
//View model
public class OrderSummary
{
public Some.Business.Logic.Order Order;
//Other methods for additional logic based on the order
//and other properties
}
Approach 2
Take only the necessary data from the business objects
//Business object in some library
public class Order
{
public int OrderNum;
public int NumOrderLines;
//...
}
//Order builder in website
public class OrderBuilder
{
public OrderSummary BuildSummaryForOrder(int OrderNum)
{
Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
//Any exception handling, additional logic, or whatever
OrderSummary obModel = new OrderSummary()
{
OrderNum = obOrder.OrderNum,
NumOrderLnes = obOrder.NumOrderLines,
}
return obModel;
}
}
//View model
public class OrderSummary
{
public int OrderNum;
public int NumOrderLines
//Other methods for additional logic based on the order
//and other properties
}
I can see the benefits and drawbacks to both, but I wonder if there’s an accepted approach? In approach 1, there’s no duplication of code around the models, but it creates a dependency on the business logic. In approach 2, you take only the data needed for the view, but you duplicate code around models.
Option 1 creates a tight-coupling between the domain model and the view. This contravenes the very problem view models are designed to solve.
A view models “reason to change” is if the view itself changes. By putting a domain model object in the view model, you’re introducing another reason to change (eg the domain changed). This is a clear indication of a violation of the single responsibility principle. Having two or more reasons to change leads to view models which require a lot of maintenance – probably more so than the perceived maintenance cost of duplication across domain/view models.
I would always advocate approach 2. It’s often the case that view models might look very similar, even identical to domain model objects, but the distinction as I mentioned is their differing reasons for change.
5
Option 1 is preferable as it avoids code duplication. That’s it.
If the domain model changes significantly , it’s almost certain that the view will have to change anyway. With option 2, then you have to change the view model AND the builder as well as the view itself. That kind of thing is absolute poison for maintinability. YAGNI.
The point of having a separate view model is to keep state that is meaningful only for the view (e.g. what tab is currently selected) separate from the business model. But the business data itself should be reused rather than duplicated.
5
Principles and mantras are sometimes valuable for guiding design… but here’s my practical answer:
Imagine your view models being serialized into JSON or XML. If you try and serialize your domain models you’re going to end up with a hideous mess of text and most likely run into issues with circular references and other issues.
The purpose of a view model is not to group domain models together so that the view can consume them. Instead the view model should be a completely flat model of the view… the actual thing you’re looking at on the screen. Your view logic should only be concerned with structuring the data that is present in the view model.
Ideally your view model should be composed almost entirely of pre-formatted strings. Think about it… you don’t even want a DateTime or decimal in your view model because then you’re stuck doing formatting logic in C#, Javascript, Objective-C, etc.
6