Discussion
Scalability
We said that scalability, i.e. how well a site can handle a large number of clients, is beyond the scope of this study.
However, there is another type of scaling which we can consider - how well will the framework's architecture scale to large, complex web sites?
- JavaServer Faces
- Backing beans go a long way to cleanly separating concerns, but a problem arises with navigation. This is actually defined in two different places;
complex navigation decisions are made by bean methods, while the actual navigation performed is defined in the application's configuration file.
A complex site will probably have dedicated navigation beans, resulting in the navigation logic being needlessly divided between beans and the configuration file.
Furthermore, the verbose syntax of the XML configuration would become difficult to manage as the site expands.
- Tapestry, Stripes
- A chief design goal of Stripes and Tapestry 5 was to avoid this configuration problem. There is no XML, and much of the configuration is by convention.
As can be seen from the source, this goal has been achieved.
- Although Stripes, like ASP.NET, stores "wizard" state on the page as hidden fields, the architecture does not tend to excessive state data.
- RIFE
- The XML links form a neat flow and data graph; with a suitable GUI, this might be manageable.
However, it is difficult to see how the XML-serialised graph could be manually maintained for a large site.
The situation is much better with continuations, where everything is handled as ordinary Java code.
- Wicket
- The component and model approach provides very neat encapsulation; large Wicket codebases are easy to manage.
- ASP.NET
- A design goal of ASP.NET was to make web programming "easy", like visual desktop programming.
Hence, the framework will persist control values across POSTs, and events can be wired up, just like desktop GUI applications.
Unfortunately, this abstraction is broken. HTTP bears little resemblance to the Windows message pump; user events will be queued up with builtin control events and framework events,
then executed in a very complex and poorly documented sequence during POST. (Leon Andrianarivony lists 62 events in the ASP.NET page lifecycle.)
Compounding the problem is that control values are not restored immediately, but during one of the framework events.
This cascading complexity of interacting events can be very difficult to manage, and cause problems which are very hard to solve.
- Additionally, the default behaviour of storing state in the page is only relevant when all the information the application needs is in the current or previous page's ViewState.
(ASP.NET makes the state of a POSTing page available to the POSTee.) Anything more complex requires the Session or persistent storage, leaving the developer in the same position as with PHP.
In particular, a large amount of information in the ViewState may cause the HTTP requests and responses to bloat to an unacceptable size.
Once ViewState is turned off, the developer will also have to manually manage some control behaviours which would otherwise have been automatic.
- ASP.NET MVC
- The MVC framework has none of these problems. Additionally, strong typing alleviates the problems of Rail's implementation (see below).
- PHP
- As can be seen from the source, even this simple application has led to a mess of PHP code interspersed with HTML.
Whilst there are no intrinsic problems with PHP that would complicate scaling, simply managing the code could become a nightmare.
- Ruby on Rails
- Rails presents a much prettier picture for the execution of complex site designs.
The clean separation of model, view, and controller, makes for a very modular and easily manageable application.
A sole concern is the model's data types, as discussed above.
- Seaside
- Seaside's components are an excellent form of composability; they enable large web applications to be managed with the same ease
as a single tier system.
- WASH
- WASH programs are straightforward Haskell programs, and can be managed in the same way as any other large program.
- Like PHP, WASH does not separate code from HTML. However, judicious use of Haskell functions to construct the HTML,
along with Haskell's functional composition and excellent combinators, can somewhat alleviate this problem.
- The interaction log is a source of concern. This grows at an alarming rate as the application proceeds, and could become a formidable barrier to complex, long-running programs.
Furthermore, although performance has been specifically excluded, it must be pointed out that CGI is highly inefficient;
the server must start a new process to serve every request. The use of Wash Server Pages is a possible alternative.
- Arc
- An Arc website is just another Arc program. As in WASH, all the usual techniques for managing large programs are equally applicable
to large websites. Similarly, standard combinators can be used to structure the logic and presentation.
Components and Templates
The authors of some component orientated frameworks argue that,
by managing state, persistent components bring easier development and better organised code.
For example, clicking previous on screen B will fill in the fields on screen A with their correct values, without any specific coding.
However, template orientated frameworks generally achieve the same benefits by binding a model class to template fields (except WASH and PHP, which strictly speaking isn't a framework - see below).
It would seem that, in most common scenarios, components are an extra layer of complexity which brings little benefit.
Seaside's components, however, are of a different nature. Behaving like the persistent components of a desktop application,
they bring the promise of component-based development to the web tier.
Designer Friendly Templates
Some frameworks are of the opinion
that HTML templates should be produced by designers,
which programmers will then tweak to work with their logic.
This would appear to be a mistake. Business analysts may assist with domain modelling,
but do not specify a program's data structures. Similarly, a web designer can specify what a page should like, but should not be responsible for producing the actual HTML templates;
this is part of the programmer's domain. Technically minded designers who feel left out can play with CSS to their hearts' content.
(HAppS takes this to an extreme;
a handler can output Haskell data types, which are automatically streamed into XML, then transformed for display with XSLT.)
Model-View-Controller Pattern
The model-view-controller pattern is an important separation of concerns.
Briefly:
- Model
- Holds all the state information (which may include business logic)
- View
- Interacts with the user, displaying a particular view of the model and forwarding input to the controller
- Controller
- Processes user input, updates the model, and selects a view.
We will now investigate the extent to which the various frameworks support this pattern.
Model
The most striking difference between the different frameworks is how they handle the model.
- JavaServer Faces, Tapestry, Stripes
- Backing beans provide the data model, with automatic conversion between HTTP strings and the model's Java types.
- RIFE
- RIFE is very flexible; simple Java classes can be used as the model, with the meta data in a separate class, or these can be combined.
- Wicket
- Any Java classes can be used as the model.
- ASP.NET
- Although there is no model as such, ASP.NET controls are exposed as strongly typed properties to the code-behind.
This works well in simple scenarios.
- ASP.NET MVC, Seaside
- Any classes can be used as the model.
- PHP
- There is no model; all data is manually copied into and out of session variables.
- Ruby on Rails
- The model is a set of classes in the models directory. With a little coding, ActiveRecord can easily keep the model synchronised with the view and returned data.
However, studying app/models/Name.rb will reveal a problem. HTTP uses plain text, and the model fields are therefore all set to strings.
ActiveRecord will automatically convert these to/from the database types when retrieving or persisting data, but this does not help the application logic.
The solution used here was write an explicit setter for converting enterSurname to a boolean.
- The problem here is not dynamic typing; there is no type mismatch for a compiler to find. Rather, the problem is a lack of metadata.
All the Java frameworks can recognise (through reflection) that a bound field is boolean, and perform the required conversions automatically.
Ruby does not support metadata, so ActiveRecord cannot determine the desired conversion.
- WASH, Arc
- There is no automatic model. Instead, the session state is held in custom data types, which are passed as parameters to each CGI action/continuation.
Widget data must be manually copied to and from the model at each stage.
View
- JavaServer Faces, Stripes
- Use JSP, a Java templating language.
- Tapestry, ASP.NET
- Elements in the templates indicate components, which are passed any attribute values by the runtime engine,
and generate markup when requested. The runtime then replaces the component declaration with its output.
- ASP.NET MVC
- Although ASP.NET MVC uses Web Forms as the default view engine, it is feasible - and common - to use simple
HTML helpers instead of components.
- RIFE
- RIFE has an interesting approach to views, with a logic-free template that is manipulated by the controller.
- Wicket
- Wicket uses HTML templates with attributes to indicate components; there is no other code in the HTML files.
- PHP, Ruby on Rails
- A free mixture of code and literal HTML.
- Seaside
- All the output is generated by components' renderContentOn: method.
- WASH
- Haskell functions (which may be created by the preprocessor from inline HTML templates) construct a well-formed document in the HTML monad.
- Arc
- The HTML library creates trees of HTML tags. It is worth noting that the quality of Arc's HTML is atrocious, but this may improve with time.
Controller
A true controller has access to an updated model (although it may have to perform this update itself), and then decides what to output.
Hooks in a Page control are not a proper implementation of the MVC pattern, as the page (view) is selected and instantiated
before the "controller" can take control.
- JavaServer Faces
- A suitable combination of faces.config, action attributes, and backing beans can provide a (convoluted) controller.
- Tapestry
- Tapestry uses a simple component architecture; everything is handled by events in the page's code.
- Stripes
- An ActionBean can serve as a controller of sorts; this is indeed what src/app/App.java does in the sample application.
However, ActionBeans must be hard-wired to particular views (in the stripes:form beanclass attribute).
- RIFE
- RIFE's elements are controllers, where the display of templates in controlled programatically.
- Wicket
- Wicket is components all the way down, and all controller logic resides in component events.
- ASP.NET
- Not only does ASP.NET have no model, there is no controller. Microsoft recognise this, and suggest a variety
of awkward
workarounds, but a true controller can only be acheived by writing a
custom HTTP handler.
- ASP.NET MVC, Ruby on Rails
- The MVC pattern is fully implemented.
- PHP
- Simply beginning a PHP page with code can make it a controller.
- Seaside
- The root component of the test application is an example of how a Seaside component can act as a controller.
However, WATask is more suited to complex workflows.
- WASH
- Each CGI action is effectively a controller.
- Arc
- Each handler or continuation is effectively a controller.
PHP
Finally, it must be pointed out that this study has been a little harsh on PHP. Ruby on Rails is a framework for the Ruby language.
Similarly, JSF and ASP.NET can be considered frameworks for Java and .NET, building on the basic libraries.
Although there are many third party frameworks for PHP (there is even a PHP on Rails), we have only considered the raw language with its standard libraries.
This is because the other languages considered are general purpose; PHP and its libraries are designed to power web sites.
No particular web framework for PHP has become popular, probably because they use PHP as just another scripting language, and other common scripting languages are more suited.