Re: Keep your peanut butter out of my chocolate.

2,129 views
Skip to first unread message

Magnus Holm

unread,
Jan 26, 2012, 4:55:21 AM1/26/12
to guardians-o...@googlegroups.com, br...@metabahn.com
(Response to http://notmagic.org/2012/01/23/peanut_butter_chocolate;
sent to the author + a mailing list)

> The view layer in web app frameworks is broken. Every framework has a template language, and every template language has the same flaw: views contain both structure and logic. This flaw annoys me as a developer, and here's why.

> Let me start by making the distinction between structure and logic. Structure defines what makes up the view. Logic defines how data goes into the view. Break the view down and it consists of nothing more than some arbitrary structure populated with data. Remove the data and you're left with an empty structure.

This is a flawed definition because logic also changes structure:

<div class="results">
<% if @results.empty? %>
<h2>Sorry, your search returned no results</h2>
<% else %>
<% @results.each do |res| %>
<div class="result">
<h3><%= link_to res.title, res %></h3>
<p><%= res.snippet %></p>
</div>
<% end %>
<% end %>
</div>

What's the structure here? Are there several structures here? Where's
the data? "Structure defines what makes up the view" is also very
vague. Doesn't the data "make up the view"? It seems to me that you're
thinking about structure as something static (like a house's
foundation); and then it's very easy to distinguish between the
structure (the rooms, the walls etc) and the data (what humans bring
into the room). However, templates are more dynamic than that: No room
can fit all our data, so we build a new house every time instead.

> In most cases the data to be presented is dynamic. So, instead of hardcoding data in the view, logic is added to describe how the structure will be populated with data. In today's template languages, this view logic is mixed in with the view structure itself.

Templates are not static "things" that you can touch and re-organize.
A templates is something that *builds* a structure depending on the
data right there. Logic doesn't merely "populate structure with data";
logic forms the structure to fit the data. You can consider certain
components of the template to be static (e.g. the .result-div always
have the same structure), but the overall template must be understood
as a *process*; gluing together several parts.

It's not enough to look at parts/structures itself; if you don't see
how they are glued together, how can you understand the result? The
reason "view logic" is mixed with "view structure" is, as mentioned
before, the logic decides the structure. If you have to know both to
know everything, isn't it better to have it all in the same place?

> For example, in Rails, an ERB template contains an HTML structure along with Ruby code that is responsible for adding data to the structure. The result is a view that, prior to rendering, contains both structure and logic. Structure and logic are closely related, but they also serve two very different purposes. Putting them in the same place leads to some real problems.

> First, this causes development roles to become unclear. View structure is a design concern and view logic is a programming concern. When they're combined, the roles of designer and programmer become fuzzy. The front-end designer's responsibility is to create the view structure, since structure is very much tied to the look and feel of an app. On the other hand, view logic is the responsibility of the back-end programmer. A designer shouldn't have to understand or even think about view logic when creating the view; it's a separate concern. Yet when placed together in the view, structure and logic can't be thought of as independent things.

> This is an issue even in a team without clearly defined designer/programmer roles. And really it's for the same reason. Combining two separate concerns in the view muddies the water no matter how large or small a team is.

Just so we agree: "view logic" is very different from "domain logic"
or "controller logic"; "view logic" is the logic that builds up the
view.

How can that view logic *not* be a front-end designer's
responsibility? View logic is about *how* the view is constructed.
Designers are very concerned about the full result; they don't work on
a "this component should look like this, while this component should
look like this". Combining components to create a consistent design is
their job! They *have* to understand how the tool their working with
combines components. I'd argue that putting the view logic within the
same file as the "structure" makes it even clearer what's going to
happen. There's more cognitive load to realize that `<div
id="header"></div>` includes an external file than `<%= render :header
%>`. The designer have to understand what's happening anyway, why not
make it explicit?

> Once logic is added to structure, one can't take a step back to see the unadulterated version of the view. Structure essentially becomes lost. This means the designer really has nothing to own, creating a process that isn't very designer-friendly. Problems also come up when it comes to changing existing views.

Well, what is a good "unadulterated version of the view"? A view is a
complex thing; as you said, it consists of structure and logic. Why is
structure essentially lost? Yes, if you push too much general logic
into the view you have a problem, but then you move things into
helpers/presenters etc. The only reason you should have complex view
logic is if the view is complex; hiding the complexity in other files
doesn't solve the problem.

> Without the ability to separate logic from structure it's impossible to refactor either one in isolation. They are so intertwined that when a change must be made, there is no other option than to change both at the same time. This makes the refactoring process much more complicated and error prone than it needs to be. Separating concerns is an important part of the refactoring process and it simply isn't possible with a view layer that consists of both structure and logic.

If you want to change the result, it's pretty clear that you'll have
to change the process too. The only case where your approach work is
if you've already thought about the problems in advance in the
logic-layer. If you haven't, then there's now two different places
where you need to refactor. Because of this, your approach encourages
generalization, and I wouldn't be surprised if you're just slowly
implementing a template language (e.g. you go from <div
class="result"> to <div class="repeat-result"> to <div
data-repeat="result">).

> Solving these problems is possible, and is actually pretty simple. It requires separating concerns by removing view logic from view structure, allowing a view to remain purely structural. View logic becomes a separate layer that acts on the view structure from outside of it. This is the approach taken in designing the Pakyow web framework, and separating structure and logic has enabled us to solve all of the problems discussed here. In addition, Pakyow developers are at least 42% less likely to be annoyed. It'll change your life.

> This is simply an introduction to the concept of separating view logic and view structure. The implementation details will be the topic of a future post. If you have any comments or would like to discuss this post feel free to send me an email or find me on Twitter.

// Magnus Holm

bryanp

unread,
Jan 27, 2012, 10:28:40 AM1/27/12
to guardians-o...@googlegroups.com, br...@metabahn.com
First, thanks for the response. I appreciate the questions and the opportunity to bring clarity to some of the points in my original post. Hopefully this discussion will continue as that's the most likely way to encourage innovation.

> Templates are not static "things" that you can touch and re-organize. A templates is something that *builds* a structure depending on the data right there. Logic doesn't merely "populate structure with data"; logic forms the structure to fit the data. You can consider certain components of the template to be static (e.g. the .result-div always have the same structure), but the overall template must be understood as a *process*; gluing together several parts.

Of course logic changes structure. View logic molds the view structure to fit the data being applied. Ultimately, the view structure will pretty closely match the data structure being applied. In your example, if there are three results, there will be three list items; if there are no results, there will be no list items. The empty message is the only exception to this rule.

Let's look at an example in Pakyow. Here's the view structure:

<div class="results">
  <h2 itemprop="result[empty_message]">Sorry, no results.</h2>
  
  <div class="result">
    <h3 itemprop="result[title]">
      The title of a result.
    </h3>
    
    <p itemprop="result[snippet]">
      Something describing the result would go here.
    </p>
  </div>
</div>

And here's the view logic:

results = […]

view.find('.results').in_context {
  context.find('.empty').remove if results.empty?
  context.find('.result').repeat_for(results, :to => :result)
}

Here the view is readable and easy to modify. It stands completely on it's own. The logic is also easy to understand. Notice also that the logic mimics the structure that it's modifying. We think this keeps things clear and is a big win overall.

> It's not enough to look at parts/structures itself; if you don't see how they are glued together, how can you understand the result? The reason "view logic" is mixed with "view structure" is, as mentioned before, the logic decides the structure. If you have to know both to know everything, isn't it better to have it all in the same place?

My argument isn't about where view logic should live. I've seen libraries that simply move view logic from the view to a Ruby class, but that isn't enough. Pakyow's goals take this idea a step further. We think that view logic should *never* be tied to structure and that they can (and should) be *completely* separated.

In a view, structure most often represents data (there are edge cases, of course). If we inform the structure as to what this data is, we provide a hook for logic to manipulate the structure to fit the data. In Pakyow, this data-awareness is built in with declarative labels -- no logic needed. The goal is for the view to not be directly (in an ERB sense) manipulated, but to perform manipulations based on the data that the view represents.

We don't have this quite right yet. Even in my example above the logic and structure are tied together in that class names are used to identify parts of the view. 0.8 will address this head on. Here's how the view could be rewritten using 0.8 concepts:

<div class="results" itemprop="results" itemscope>
  <h2 itemprop="empty">Sorry, no results.</h2>
  
  <div class="result" itemprop="result" itemscope>
    <h3 itemprop="title">
      The title of a result.
    </h3>
    
    <p itemprop="snippet">
      Something describing the result would go here.
    </p>
  </div>
</div>

And here's the updated logic:

results = […]

view.data(:results).in_context {
  context.data(:empty).remove if results.empty?
  context.data(:result).repeat_for(results, :to => result)
}

This matches our goal exactly by only hooking in to what data is represented in the structure, not the structure itself.

> Just so we agree: "view logic" is very different from "domain logic" or "controller logic"; "view logic" is the logic that builds up the view.

I absolutely agree.

> How can that view logic *not* be a front-end designer's responsibility? View logic is about *how* the view is constructed. Designers are very concerned about the full result; they don't work on a "this component should look like this, while this component should look like this". Combining components to create a consistent design is their job! They *have* to understand how the tool their working with combines components. I'd argue that putting the view logic within the same file as the "structure" makes it even clearer what's going to happen. There's more cognitive load to realize that `<div id="header"></div>` includes an external file than `<%= render :header %>`. The designer have to understand what's happening anyway, why not make it explicit?

Designing a structure and describing how it reacts to data can quite easily be thought of as separate things, in my opinion. Yes, there's still a learning curve, but it's closer to a designer's current expertise.

> Well, what is a good "unadulterated version of the view"? A view is a complex thing; as you said, it consists of structure and logic. Why is structure essentially lost? Yes, if you push too much general logic into the view you have a problem, but then you move things into helpers/presenters etc.

Views can be complex yes. But clarity can be brought by enforcing separation. This allows structure to stand completely on it's own to easily be seen for what it is.

> The only reason you should have complex view logic is if the view is complex; hiding the complexity in other files doesn't solve the problem.

Again, we aren't just moving view logic somewhere else. Complex view logic is actually simplified once the distinction is made between structure and logic.

> If you want to change the result, it's pretty clear that you'll have to change the process too. The only case where your approach work is if you've already thought about the problems in advance in the logic-layer. If you haven't, then there's now two different places where you need to refactor.

If we do our job right, structure can change all day long but as long as it represents the same data, no refactoring of the logic will be needed. When creating a view, the designer shouldn't have to think about anything more than what structure should be there and what data that structure represents.

> Because of this, your approach encourages generalization, and I wouldn't be surprised if you're just slowly implementing a template language (e.g. you go from <div class="result"> to <div class="repeat-result"> to <div data-repeat="result">).

Up to this point, adding data awareness to the view through declarative labels has been enough. I don't see a need to go beyond this.

Bryan
Reply all
Reply to author
Forward
0 new messages