Loud Thinking: about MVC

I finally got around to planning the next article in my "Introduction to bfg" series which will hope be done in a week or so. At one point when writing it I was trying to describe why I used the context models in such an "unconventional way". I concluded that at least for me, it is because patterns like Model View Controller, and Model View Template as implemented by some web frameworks today only provide a partial solution to managing application complexity.

Back when I was doing ASP.net applications, I hated life. The reason was, that I typically had to deal with these Page objects that fired events and it often led to a whole mess of code that did not lend itself to being easily understood by the author or other team members. It sucks just like a moderately complex VB6 or Delphi application sucks. I tried to divide up the work into user controls to hopefully scale back the suck, but that took a lot of plumbing code, and at least for me it never came down to a repeatable pattern or even a reliable recipe.

Meanwhile, the smart people at Microsoft were concentrating on either new fangled web controls, or something having to do with web services. Nothing that addressed what I perceive as a need to tame the complexity of an ASP.net application.

I suspect a lot of people felt this way because one day, Rails appeared, and you could make a wiki in 10 seconds. And it was fun, and programmers felt productive again. This was a good thing. In python land of course there is pylons, turbogears, django and bfg and others, the benefits of which are very similar to rails. It's all about productivity.

And in my opinion the reason programmers feel productive in these frameworks is due to the MVC pattern or MVT pattern that they are based on.

The ORM

For example, when building an application with an MVC framework, how to persist your data or fetch it is largely taken care of by the recommended Object Relational Mapper.

Because of the lack of a ORM in ASP.net back in the day, often the first task when beginning a new web application was writing the data access layer to provide essentially what you needed from an ORM. And teams would spend an enormous amount of time engineering the data access layer so that it "could scale" and since you needed it before you could do anything else, it would typically be the one portion of the application that had a well understood pattern.

In reality though, writing your own data access layer is the kind of busy work you do when you don't want to do anything risky.Think about it. If you were just handed a pile of requirements that you didn't understand, and the powers at be wanted to see progress, keep working on that data access layer and designing the database.

It's something that you know is needed, and the stakeholders typically don't give a crap about it. So, until you fully understand what you are going to do next, keep tweaking those stored procedures and your customized connection pool.

The Template

While it is true that ASP.net has some concept of a template that is the link between your data/logic and it's presentation. Trying to figure out what context your template has can be a challenge. It's been too long now for me to remember fully how it works. I can say with some assurance it's more complex than "return a dictionary, and it will be given to your template".

I never understood how something as simple as string replacement(let's face it, that's all we do with templates) was decided to be best implemented with a specialized (undocumented) compiler and these things called "partial classes WTF?".

Though this is an endorsed pattern in ASP.net it's not a very good one in my opinion. Mere mortals can not be productive with it, thus it isn't useful.

Due to the utter lack of any clear useful patterns that ASP.net proposes, anything based on MVC or MVT should make any programmer giddy with joy. Microsoft seems to agree, after all they now have ASP.net MVC that you can download, and hopefully someday will be a first class citizen in their developer tool set.

Now, while MVC and MVT are definitely better at dividing up complexity than the now classic ASP.net and it's equivalents. I'm am not of the opinion that we have found the best solution to the problem. I feel this way because I have seen and written controller methods a mile long with plenty of code that wasn't exactly DRY.

It's not like it was planned that way, it grows to be that way, through little changes by different people over time until one day, no one knows what the code does, or remembers fully the history of why it does it. The unlucky person assigned the task only knows what it's supposed to do. And the person needs to do it in the least invasive, least risky way possible, which often means "don't re-factor", and "no time or too risky to do the correct thing"

Why does this happen? Well again, in my opinion, it is because even in a MVC framework, getting from your finely mapped database objects to your html output is still for the most part a gray area.

Think about it for a second the job of a controller method in most frameworks is to show a page, but the only hint it typically has to do it's job is some sort of request object. From that request object a controller method must gather all the data it needs and transform it into a series of attributes that a view expects. How it does this is up to you. That's a pretty big responsibility for a controller method who's only been handed a request object.

I suppose this can be a good thing. After all, a framework has no idea what your application is supposed to do. And if it made many assumptions about your application then the framework would tend to be inflexible and likely not that great. But it sure would be nice to have some direction as to other patterns to mix in their to reduce the complexity when necessary.

One unique thing about bfg is that model traversal thing. So every view method(what I would call a controller method in other frameworks) has the request but also some sort of context that's been pre-determined based on the url.

But what is this context object? Well, that's up to you the application developer. It's sometimes implied in the documentation that is should be some sort of persistent object like a row in the database, and it very well could be. But I don't think it's useful being used that way. After all, all it may save me the work of having to write "select [data] from [mapped table] where [criteria]" But sqlalchemy already makes that pretty easy.

What if instead the context object was something else entirely unrelated to any notion of persistent data. What if the context object that was handed to a view method instead had attributes like

  • the current data object to be considered the context for the view
  • a callable that will render a form with the current data object if called
  • a validate method that takes a request, validates the form data contained within and returns some indication of success or failure as well as a suggested response to return

After completing the manager interface for identity.model it dawned on me that I could probably reduce my view code significantly by establishing this pattern between my views and their corresponding context.

def view_add_user(context, request):

    if "POST" in request.params:
        result = context.create(request)
        if result.success:
            return result.response
        else:
            return {"form": form_callable(context.create_form(), {}),
                    "flash": result.message}
    else:
        return {"form": form_callable(context.create_form(), {}),
                "flash": "Add new user"}

So what are the advantages? For one, code that is consistent can be easily understood. It just so happens that the manager interface is 1 summary view with a bunch of forms. So with the exception of my summary view method, everything else essentially looks like this with very little variation. That variation could be abstracted away as well. Currently, the views.py that this is in is just shy of 250 lines. Not exactly huge but it is the biggest of all the views.py in identity.model. By the way, all the form views get routed to one common template.

So what are the disadvantages? For one I don't have a fancy name for it. And no academics have identified it as a pattern as far as I know. It makes the views(and context) more rigid by establishing a formal protocol that the context must now obey. Whether this is good or bad probably depends on the specifics of the application in question.

Could this be done in other frameworks? Sure, someone needs to write a function that should be called by the view function first to derive the context from the request. But, bfg already has it.

So the conclusion I've arrived at (I think) is that frameworks based on MVC/MVT, though useful, seem to punt back to the developer when the application gets complex. And probably appropriately so. That doesn't make my job any easier though.

Maybe view functions or controllers are expected to do too much. And maybe there's a way to split that responsibility up into something more manageable. Maybe some sort of protocol can be established so that the view/controller can coordinate the work to go from request to response, but something else(perhaps a context object) actually does this work.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><p> <h1><h2><h3><h4><h5><h6> <img>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]".
  • E-Mail addresses are hidden with reCAPTCHA Mailhide.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.