Disclaimer: only purpose of this post is to continue hot Twitter conversation about using expressions which computes something in MVC views. I don't want to say that templates are the bad thing, nor that Tomislav Capan's presentation about Spark View Engine was bad ;).
Intro: Today at Croatian WebUG, Tomislav Capan had presentation about Spark View Engine for ASP.NET. Overall speaking, presentation was ok and I learned something new, but there is one thing which bothers me in the whole idea of using templates which allows all kind of operations in applications which follows MVC pattern. Here is one example of the View written using the Spark template's syntax:
<s:for each="var p in Products">
<p class="${p.Price > 100 ? "highPrice" : "lowPrice"}">${p.Description}</p>
</s:for>
What's the problem with the above code?
Generally speaking nothing (maybe it has some syntax error, but that doesn't matter in the current context). If you need to write something fast and all you want is working code now, then the code is ok. But, if you are determine to follow MVC way of writing applications with clear separation of concerns and you want to make your code easily maintainable and testable (I'm talking about unit tests) then, you are doing it wrong.
Imagine that, after you have wrote above code, your currency significantly lose on its value. Now, the new business rule is that high price is 500. You need to change your View and change the expression p.Price > 100 to p.Price > 500. Obviously you wrote your View in a wrong way. When the business rule has changed you need to change your View instead of Controller.
The better way to accomplish the same thing is to write something like:
<s:for each="var p in Products">
<p class="${p.PriceImportance}">${p.Description}</p>
</s:for>
where PriceImportance is some property or method in Products class which returns strings highPrice or lowPrice based on evaluation of the Price > 100 expression. Expression is written somewhere in some Controller.
You can now say that I put presentation logic outside of the View. But I didn't. Only View knows what to do with the highPrice and lowPrice values and how those values are represented. Controller even doesn't know what the highPrice and lowPrice are. It doesn't know how their representation.
View is responsible to represent highPrice. It can represent it with the red color or with the yellow color. If the business rule changes and we have highPrice, mediumPrice and lowPrice where they are represented with yellow, blue and green colors respectively you need to change your highPrice CSS class from background-color: Red; to background-color: Yellow; and everything will work. The important thing is, that this change is done in the View. The business part of the change, the fact that we don't only have high and low price is done in a Controller where expression is changed to Price > 200 ? highPrice : (Price > 150 ? mediumPrice : lowPrice).
If some other View need to use the same Controller, it can freely represent highPrice as <b>highPrice!</b>!
At the end, here are my answers on some questions which occured during Twitter conversations about the same topic:
- Does Controller depends on View? - in the second solution it doesn't. If you change your view from HTML View where highPrice is name of some CSS class to Silverlight View where hightPrice is key of some XAML style, everything will still work
- Can you unit test your business logic? - yes you can in the second solution. In the first solution? I don't think so.
- Does Controller knows anything about the presentation? - no it doesn't. View will represent highPrice if it thinks that it need to be represented and it will represent it as HTML, XAML, ...
- Why can't Controller just return value (100) and let View represent the value with the help of JavaScript? - because then you just shifted business logic from backend language to JavaScript and you have your business logic in two places -> two places for doing unit testing