Hackfoofery

Alson Kemp

How do you maintain type-safety in web applications?

Note: this post really concerns statically typed languages, but my little bits of example code are in Ruby since that’s the MVC implementation I know best.

I like MVC-ish-ness for web applications

The MVC-ish aspect of Rails is much clearer than the ASP/PHP-ish world’s chaos, so I’m bought into MVC-ish as a better way.  Separating the request processing into MVC generally gets me into the right mindset when I’m looking at individual bits of code. That said, Rails’ MVC still falls to satisfy fully my cravings for a web framework.

I don’t like the lack of type-safety

As Curt and I were discussing over in my last post, I am bothered by the lack of type-safety in MVC web frameworks.  In most MVC frameworks, type-safety is ignored or broken, e.g.:

  • Rails is basically not typed and Views can access the Controller’s instance variables;
  • ASP.NET uses an object called ViewState to pass data from Controllers to Views, but you have to cast ViewState to the right type in the View [yes, I'm treating ASP.NET as MVC...];
  • Turbinado copied ASP.NET’s ViewState and inherits its issues.

I’d like to figure out a way to maintain type-safety within my web application.  In the case of dynamic languages (e.g. Ruby), I’d like “type-clarity” so that I’m nearly guaranteed of having the right type in the right place. For example [using Ruby syntax]:

The header of a web app might have a account section which behaves differently whether or not a User has been loaded into @user in the Controller.   It’s easy enough to have the section behave differently based on whether @user.nil?, but I can’t guarantee that, if the @user is not nil, @user is of type User.  *I* can certainly check the class of @user, but that leaves me open to errors at runtime when I _do_ change the type of @user and forget to update all of my type checks.

I want to be able to set the @user variable to a User in the Controller and have that type information enforced at compile time in the View.  Given an MVC-ish system, there seems to be two ways to do this:

  • Use different data types for each view and piece of a view.   This would be a nightmare since you’d have to define hundreds of separate types, build them differently and then send the right type to the right view.
  • Only ever create the values in question right near their usage, but this pushes Model fiddling and request params access down into the View, so doesn’t fit the web MVC style…

A Slight Modification to Rails’ MVC-ish-ness

Even if it isn’t the Rails MVC Way, it seems as though a reasonable way to maintain type safety would be to:

  • Don’t use Controller to set up variables for use in Views.
  • Mix more of the Controller functionality into the Models so that Models can manage creation of themselves.
  • Have Views use Models directly (and have Models cache/memo-ize themselves as necessary).
  • Use Controllers *only* for validation and routing.
  • Have Views play directly with the Models they require.

Currently, Rails’ MVC works something like the following:

---
some_controller.rb
class SomeController < ApplicationController
  def index
    @user = User.find(params[:user_id])
  end
end
---
some_view.html.erb
 
    <= HEY! Is @user **really** a User?
 
  Log in!
 
---

Instead, how about giving the Model access to the params and letting it use its business rules to set itself up:

---
some_controller.rb
class SomeController < ApplicationController
  def index
  end
end
---
some_view.html.erb
 
       <= HEY! That's almost guaranteed to work.
 
  Log in!
 
---
user_model.rb
Class User < ActiveRecord::Base
  def self.from_request
    find(params[:user_id])  <= the models is accessing the Environment...
  end
end

This is wrong, right?

Per Terrence Parr in Enforcing Strict Model-View Separation in Template Engines [PDF]:

[...] there is widespread agreement that keeping the view as simple and free from entanglements with model and controller as possible is a worthwhile ideal to strive for.
[...] Strictly separating model and view is an extremely desirable goal because it increases flexibility, reduces maintenance costs, and allows coders and designers to work in parallel.

But I think Parr’s point is really that business logic shouldn’t appear in Views not that UserModel access shouldn’t be done in a View.  Or is there some horrible breakdown that would occur if Models had access to request parameters and Controllers didn’t set up Models?

An issue that does arise with this modification is increased testing complexity when the Environment is accessible to Models.  Whereas now Models take a few parameters and then pass or don’t pass the test, allowing Models to access the Environment would mean that tests would have to set up request parameters, cookies, etc.  Simple, little, lovely Unit tests suddenly get larger and potentially uglier…

Type-safety + MVC ~= peanut butter & chocolate

Is type-safety a worthy goal to try to attain in MVC-ish web frameworks?  If so, how is the best way of doing so? In Rails, judicious application of filters and good naming schemes can get you “type-confident”, but you’re never type-safe.  Perhaps Rails has just the right blend for an MVC-ish web framework…

If MVC-ish web frameworks aren’t the way to go, what organization is better? Templating systems that only allow template markup/commands seem pretty safe, but I’d seriously miss the simplicity of mixed markup/code. ASP.NET compiles the template and then the “code behind” gets well-typed access to the XML, but I really prefer MVC to that organization.

Written by alson

March 18th, 2010 at 8:02 pm

Posted in Turbinado

with 2 comments

2 Responses to 'How do you maintain type-safety in web applications?'

Subscribe to comments with RSS or TrackBack to 'How do you maintain type-safety in web applications?'.

  1. Just a explanation. ASP.NET WebForms uses ViewState ASP.NET MVC does not. ASP.NET WebForms (usualy just called ASP.NET) and ASP.NET MVC are two different beasts. With ASP.NET MVC you have type safety when moving data from controller to view because the views can be typed. You can pass a concrete type to a view from the controller by writing return View(model) at the end of your controller action. The model variable is a concrete type that will be available in the view as the Model property. Visual Studio IDE can even give you inetllisense for your views. Because views are not compiled right away but rather at the first moment they are used the c# compiler will not issue compile time errors if you misspell something in the view but it will throw an exception on runtime. Tools like resharper (Visual Studio plugin that every .net developer must have) can spot these errors and give you warnings before you run them.

    Ivica Munitic

    15 Jun 10 at 5:18 pm

  2. Doh. Typo. Meant viewData, but got mixed up with ASP.NET’s viewState. Corrected. Relevant: http://msdn.microsoft.com/en-us/library/dd394711.aspx

    I think you’re saying that you can pass (for example) an instance of the Person “model” to the controller. While that does provide type safety, it’s a fairly trivial case. Passing a concrete type means that separate concrete datatypes would needed for nearly every view and, in any reasonably sized web app, that means hundreds of datatypes. This is my basic gripe (not with ASP.NET, but with statically types languages in general). I would love to find a way to stuff a bunch of variables into a heterogeneous collection and be able to prove that the controller and view were in-sync. Unfortunately, this looks like a seriously non-trivial problem since Views may or may not access certain parts of datatypes/structures making it difficult to understand whether an inbound datatype will satisfy completely the view.

    alson

    15 Jun 10 at 5:59 pm

Leave a Reply