Plumbing & Porcelain
I've built three or four Ajax "frameworks" in my career. I don't know if you would call them frameworks because there are really two kinds of things I mean when I say "framework". There is the extremely general purpose plumbing framework, which can do anything, but as a result leaves you to write a good bit of code and maybe pick and choose which parts you need. Some examples of plumbing type Ajax frameworks/components would include sever side frameworks like DWR, Ajax4JSF, etc. but also client side frameworks like dojo, Scriptaculous, jQuery, etc. Also most of the Rails type server frameworks have Ajax support built in: e.g., Grails and Django.
Then there are the porcelain frameworks. The problem with the plumbing frameworks is that they can do anything, so you're presented with a big toolbox full of neat things and a bunch of pipes, but you have to put it all together. A porcelain Ajax framework is usually built by an organization for their apps, or even for a single application. Sometimes they become an entire platform internal to the application and sometimes no thought is put into developing the porcelain and you end up with each developer doing Ajax in their own way.
Inner Platforms & Thoughtless Scaffolding
Some developers recoil at the thought of internal platforms. They tend to make a few use cases easy but then require horrible black voodoo workarounds when trying to do something the original architects didn't think of. Other developers are completely frustrated with the thoughtlessly thrown together and generally rickety Ajax platform they've been trying to maintain.
An inner platform sometimes arises when otherwise highly competent enterprise (read: Java or .Net) developers first encounter front-end programming and dynamic languages. They are used to structure and standards and ACID properties and so forth. This lack of static types and compilers instills a certain sense of fear and the only way to conquer that fear is to create "coding standards" and, then when those are not followed, to create an internal UI framework that forces everyone to code in a certain way, without closures, functional programming, overloaded operators, dynamic dispatch, and all those other ugly hacks that are bad for performance.
Other times, an inner platform is really just the result of well intentioned senior developers trying to make things easier for the more junior members of the team. Unfortunately, they often do so by hiding important information and you end up with a very leaky abstraction that is buggy and that the users (programmers other than the authors) are completely helpless to fix because they have no experience with the underlying framework. The points is that there are many ways to end up with an inner platform that does more harm than good.
Thoughtless scaffolding on the other hand is just the result of... thoughtlessness. Most often it's because a programmer without much experience and no real computer science training is writing all the code and whatever tool he's read about today is the perfect tool for the job. But it also happens when a new project is started and the technical leadership fails to identify the patterns that should be used. Maybe they start off with some vague notion like, "JSON is the answer," and become committed to a bad strategy early on and never have the time or the courage to go back and clean it up.
Both extremes have problems, but they also have advantages. For the tasks it was built for, the inner platform makes adding new Ajax features extremely simple. On the other hand, the thoughtless scaffolding never gets in the way. Because very little of it is connected, it's easy to rip part of it out and build something new entirely.
We want to find a middle ground between over engineered and thrown together, but where that middle ground lies can depend a lot on whether maintenance or new features are your priority. Instead we will focus on the core principles and features that any porcelain Ajax framework should encompass.
Core Principles
These are the core principles I have learned through trial an error over the past 5 years, building and using frameworks across the spectrum from somewhat thoughtless to a massive inner platform:
- Remember it's a web app - It's not a desktop app. You're probably not build the next Gmail, so stick with what works. Use semantic markup, keep the pages simple and light, and use progressive enhancement so the app works without JS. For public facing apps this is a must obviously, but even for internal apps where you know that your users have a decent browser with JS enabled, you will stay out of trouble by keeping it simple.
- Do everything you can on the server - Especially rendering HTML. JSON is overrated. jQuery's load method can do 95% of what you need in one line of JS. For the most part, JS should not be producing HTML. Things go much more smoothly when all HTML is produced server side because then the logic to generate the markup doesn't need to be duplicated. There are exceptions, like if you have a feature using 3rd party code, like a Google Map, that simply will not work without JavaScript. Then it might be necessary to generate some of the UI in JS. If you must generate HTML in JS, use a templating framework. If you are concatenating anything more than 100 characters of HTML together, you're making a mess of your code.
- Keep the porcelain thin - Do not under any circumstances create more than one layer of abstraction between the code and the core web technologies. Most of the HTML output of your application should correspond to HTML in a template file. Taglibs are fine, but try to use your templating language even inside of them. Do not create a system where the developer doesn't have to know HTML. The same goes for JavaScript and CSS. Do not write code to generate CSS ever. You may use a framework that generates CSS to reduce repetition, but try to only use the big ones with a good community. Do not generate JavaScript code ever. Generate inline JSON instead and have your scripts consume it to determine behavior dynamically. Use stateful CSS rules.
- Cover all the pipes - When you realize that to make it all work you need every load() call to add some parameter, or check some condition, you better hope you can go to one place in your code and make that change. Hopefully the plumbing provides good hooks, or there is some AOP/IoC magic that can cover it. Otherwise, you may have to create very very thin wrappers around plumbing functionality. Not in the hopes of portability (you will never port this app to a different framework, it will be rewritten), but so that you can funnel all calls to that feature through one place.
Core Features
So what features does a HTML based, server oriented porcelain framework provide?
- Component Refresh - This is the core. There needs to be a simple way to designate an area of the page as a component and an easy way to make a call to refresh its HTML. This may be as simple as using jQuery's load with a selector to get the desired page fragment, although it's nice if the server can do the minimal amount of work necessary to render the HTML you need.
- Multi-Component Refresh - Sometimes you need to refresh several parts of the page whose only common parent is the body element. You could just reload the whole page, but for an action that only affects part of the page that's both inefficient and bad UX. You could also use multiple requests, but if there are more than 2 components, then many browsers will block the 3rd request until one of the others return. There is probably also a server part of the framework that allows the components to share data instead of making redundant calls.
- JSON - Not to be consumed by Ajax calls, but to inline as script tags in the page. Most frameworks can take the same data model you pass to your templates and convert it to JSON automagically for your JS to consume too. You probably will want to include it inside your components' HTML so you can update the JS state when a component is refreshed.
Next Time Let's Build One
Soon, I hope to have some example code for a simple Grails porcelain framework. Actual code can be a lot more informative and illuminating than pithy maxims.
No comments:
Post a Comment